upload
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
"""presenter app manager module"""
|
||||
|
||||
import time
|
||||
import threading
|
||||
import logging
|
||||
from common.channel_manager import ChannelManager
|
||||
|
||||
# Heartbeat timeout, exceeding the limit, the socket will disconnect
|
||||
HEARTBEAT_TIMEOUT = 100
|
||||
|
||||
class App():
|
||||
'''App class, When receive an app request from
|
||||
Presenter Agent, creat an object.
|
||||
'''
|
||||
def __init__(self, app_id, conn=None):
|
||||
self.app_id = app_id
|
||||
self.heartbeat = time.time()
|
||||
self.socket_fd = conn.fileno()
|
||||
# set timeout 1 second
|
||||
conn.settimeout(1)
|
||||
self.socket = conn
|
||||
self.frame_num_dict = {}
|
||||
|
||||
class AppManager():
|
||||
'''A class provides app management features'''
|
||||
__instance = None
|
||||
channel_manager = None
|
||||
app_list_lock = threading.Lock()
|
||||
app_list = []
|
||||
thread_switch = False
|
||||
|
||||
def __init__(self):
|
||||
"""init func"""
|
||||
|
||||
|
||||
def __new__(cls):
|
||||
"""ensure only a single instance created. """
|
||||
if cls.__instance is None:
|
||||
cls.__instance = object.__new__(cls)
|
||||
cls.channel_manager = ChannelManager([])
|
||||
cls._create_thread()
|
||||
return cls.__instance
|
||||
|
||||
@classmethod
|
||||
def _create_thread(cls):
|
||||
"""_create_thread."""
|
||||
|
||||
thread = threading.Thread(target=cls._app_thread)
|
||||
thread.start()
|
||||
|
||||
@classmethod
|
||||
def _app_thread(cls):
|
||||
"""background thread to process video"""
|
||||
logging.info('create app manager thread')
|
||||
while True:
|
||||
if cls.thread_switch:
|
||||
break
|
||||
for i in range(len(cls.app_list)):
|
||||
if time.time() - cls.app_list[i].heartbeat > HEARTBEAT_TIMEOUT:
|
||||
app_id = cls.app_list[i].app_id
|
||||
cls.channel_manager.unregister_one_channel(app_id)
|
||||
del cls.app_list[i]
|
||||
logging.info("unregister app: %s", app_id)
|
||||
time.sleep(1)
|
||||
|
||||
def set_thread_switch(self):
|
||||
AppManager.thread_switch = True
|
||||
|
||||
def register_app(self, app_id, socket):
|
||||
"""
|
||||
API for registering an app
|
||||
Args:
|
||||
app_id: app id, must be globally unique
|
||||
socket: a socket communicating with the app
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].app_id == app_id:
|
||||
return False
|
||||
|
||||
app = App(app_id, socket)
|
||||
self.app_list.append(app)
|
||||
self.channel_manager.register_one_channel(app_id)
|
||||
logging.info("register app: %s", app_id)
|
||||
return True
|
||||
|
||||
def unregister_app_by_fd(self, sock_fileno):
|
||||
"""
|
||||
API for unregistering an app
|
||||
Args:
|
||||
sock_fileno: sock_fileno is binded to an app.
|
||||
Through it, find the app and delete it.
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].socket_fd == sock_fileno:
|
||||
app_id = self.app_list[i].app_id
|
||||
self.channel_manager.unregister_one_channel(app_id)
|
||||
del self.app_list[i]
|
||||
logging.info("unregister app: %s", app_id)
|
||||
break
|
||||
|
||||
def get_socket_by_app_id(self, app_id):
|
||||
"""
|
||||
API for finding an app
|
||||
Args:
|
||||
app_id: the id of an app.
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].app_id == app_id:
|
||||
return self.app_list[i].socket
|
||||
return None
|
||||
|
||||
def get_app_id_by_socket(self, sock_fd):
|
||||
"""
|
||||
API for get app id by socket
|
||||
Args:
|
||||
sock_fd: sock_fd is binded to an app.
|
||||
Through it, find the app and delete it.
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].socket_fd == sock_fd:
|
||||
return self.app_list[i].app_id
|
||||
return None
|
||||
|
||||
def is_app_exist(self, app_id):
|
||||
"""
|
||||
API for checking if the app exist
|
||||
Args:
|
||||
app_id: the id of an app.
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].app_id == app_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_app_num(self):
|
||||
"""
|
||||
API for getting the number of apps
|
||||
Args: NA
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
return len(self.app_list)
|
||||
|
||||
def set_heartbeat(self, sock_fileno):
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].socket_fd == sock_fileno:
|
||||
self.app_list[i].heartbeat = time.time()
|
||||
|
||||
def increase_frame_num(self, app_id, channel_id):
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].app_id == app_id:
|
||||
if channel_id in self.app_list[i].frame_num_dict:
|
||||
self.app_list[i].frame_num_dict[channel_id] += 1
|
||||
else:
|
||||
self.app_list[i].frame_num_dict[channel_id] = 1
|
||||
|
||||
def get_frame_num(self, app_id, channel_id):
|
||||
with self.app_list_lock:
|
||||
for i in range(len(self.app_list)):
|
||||
if self.app_list[i].app_id == app_id:
|
||||
if channel_id in self.app_list[i].frame_num_dict:
|
||||
return self.app_list[i].frame_num_dict[channel_id]
|
||||
else:
|
||||
return 0
|
||||
return 0
|
||||
def list_app(self):
|
||||
"""
|
||||
API for listing all apps
|
||||
Args: NA
|
||||
"""
|
||||
with self.app_list_lock:
|
||||
return [self.app_list[i].app_id for i in range(len(self.app_list))]
|
||||
@@ -0,0 +1,227 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
|
||||
"""presenter channel manager module"""
|
||||
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
from threading import get_ident
|
||||
from common.channel_manager import ChannelManager
|
||||
|
||||
# thread event timeout, The unit is second.
|
||||
WEB_EVENT_TIMEOUT = 2
|
||||
# thread event timeout, The unit is second.
|
||||
IMAGE_EVENT_TIMEOUT = 10
|
||||
|
||||
# heart beat timeout, The unit is second.
|
||||
HEARTBEAT_TIMEOUT = 100
|
||||
|
||||
class ThreadEvent():
|
||||
"""An Event-like class that signals all active clients when a new frame is
|
||||
available.
|
||||
"""
|
||||
def __init__(self, timeout=None):
|
||||
self.events = {}
|
||||
self.timeout = timeout
|
||||
|
||||
def wait(self):
|
||||
"""Invoked from each client's thread to wait for the next frame."""
|
||||
ident = get_ident()
|
||||
if ident not in self.events:
|
||||
# this is a new client
|
||||
# add an entry for it in the self.events dict
|
||||
# each entry has two elements, a threading.Event() and a timestamp
|
||||
self.events[ident] = [threading.Event(), time.time()]
|
||||
return self.events[ident][0].wait(self.timeout)
|
||||
|
||||
def set(self):
|
||||
"""Invoked by the camera thread when a new frame is available."""
|
||||
now = time.time()
|
||||
remove = None
|
||||
for ident, event in self.events.items():
|
||||
if not event[0].isSet():
|
||||
# if this client's event is not set, then set it
|
||||
# also update the last set timestamp to now
|
||||
event[0].set()
|
||||
event[1] = now
|
||||
else:
|
||||
# if the client's event is already set, it means the client
|
||||
# did not process a previous frame
|
||||
# if the event stays set for more than 5 seconds, then assume
|
||||
# the client is gone and remove it
|
||||
if now - event[1] > 5:
|
||||
remove = ident
|
||||
if remove:
|
||||
del self.events[remove]
|
||||
|
||||
def clear(self):
|
||||
"""Invoked from each client's thread after a frame was processed."""
|
||||
self.events[get_ident()][0].clear()
|
||||
|
||||
class ChannelHandler():
|
||||
"""A set of channel handlers, process data received from channel"""
|
||||
def __init__(self, channel_name, media_type):
|
||||
self.channel_name = channel_name
|
||||
self.media_type = media_type
|
||||
self.img_data = None
|
||||
self._frame = None
|
||||
self.thread = None
|
||||
self._frame = None
|
||||
# last time the channel receive data.
|
||||
self.heartbeat = time.time()
|
||||
self.web_event = ThreadEvent(timeout=WEB_EVENT_TIMEOUT)
|
||||
self.image_event = ThreadEvent(timeout=IMAGE_EVENT_TIMEOUT)
|
||||
self.lock = threading.Lock()
|
||||
self.channel_manager = ChannelManager([])
|
||||
self.rectangle_list = None
|
||||
|
||||
if media_type == "video":
|
||||
self.thread_name = "videothread-{}".format(self.channel_name)
|
||||
self.heartbeat = time.time()
|
||||
self.close_thread_switch = False
|
||||
self.fps = 0
|
||||
self.image_number = 0
|
||||
self.time_list = []
|
||||
self._create_thread()
|
||||
|
||||
def close_thread(self):
|
||||
"""close thread if object has created"""
|
||||
if self.thread is None:
|
||||
return
|
||||
|
||||
self.set_thread_switch()
|
||||
self.image_event.set()
|
||||
logging.info("%s set _close_thread_switch True", self.thread_name)
|
||||
|
||||
def set_heartbeat(self):
|
||||
"""record heartbeat"""
|
||||
self.heartbeat = time.time()
|
||||
|
||||
def set_thread_switch(self):
|
||||
"""record heartbeat"""
|
||||
self.close_thread_switch = True
|
||||
|
||||
def save_image(self, data, width, height, rectangle_list):
|
||||
"""save image receive from socket"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.rectangle_list = rectangle_list
|
||||
|
||||
# compute fps if type is video
|
||||
if self.media_type == "video":
|
||||
while self.img_data:
|
||||
time.sleep(0.01)
|
||||
|
||||
self.time_list.append(self.heartbeat)
|
||||
self.image_number += 1
|
||||
while self.time_list[0] + 1 < time.time():
|
||||
self.time_list.pop(0)
|
||||
self.image_number -= 1
|
||||
if self.image_number == 0:
|
||||
break
|
||||
|
||||
self.fps = len(self.time_list)
|
||||
self.img_data = data
|
||||
self.image_event.set()
|
||||
else:
|
||||
self.img_data = data
|
||||
self.channel_manager.save_channel_image(self.channel_name,
|
||||
self.img_data, self.rectangle_list)
|
||||
|
||||
self.heartbeat = time.time()
|
||||
|
||||
|
||||
def get_media_type(self):
|
||||
"""get media_type, support image or video"""
|
||||
return self.media_type
|
||||
|
||||
def get_image(self):
|
||||
"""get image_data"""
|
||||
return self.img_data
|
||||
|
||||
def _create_thread(self):
|
||||
"""Start the background video thread if it isn't running yet."""
|
||||
if self.thread is not None and self.thread.isAlive():
|
||||
return
|
||||
|
||||
# start background frame thread
|
||||
self.thread = threading.Thread(target=self._video_thread)
|
||||
self.thread.start()
|
||||
|
||||
def get_frame(self):
|
||||
"""Return the current video frame."""
|
||||
# wait util receive a frame data, and push it to your browser.
|
||||
ret = self.web_event.wait()
|
||||
self.web_event.clear()
|
||||
# True: _web_event return because set()
|
||||
# False: _web_event return because timeout
|
||||
if ret:
|
||||
return (self._frame, self.fps, self.width, self.height, self.rectangle_list)
|
||||
|
||||
return (None, None, None, None, None)
|
||||
|
||||
def frames(self):
|
||||
"""a generator generates image"""
|
||||
while True:
|
||||
self.image_event.wait()
|
||||
self.image_event.clear()
|
||||
if self.img_data:
|
||||
yield self.img_data
|
||||
self.img_data = None
|
||||
|
||||
# if set _close_thread_switch, return immediately
|
||||
if self.close_thread_switch:
|
||||
yield None
|
||||
|
||||
# if no frames or heartbeat coming in the last 100 seconds,
|
||||
# stop the thread and close socket
|
||||
if time.time() - self.heartbeat > HEARTBEAT_TIMEOUT:
|
||||
self.set_thread_switch()
|
||||
self.img_data = None
|
||||
yield None
|
||||
|
||||
def _video_thread(self):
|
||||
"""background thread to process video"""
|
||||
logging.info('create %s...', (self.thread_name))
|
||||
for frame in self.frames():
|
||||
if frame:
|
||||
# send signal to clients
|
||||
self._frame = frame
|
||||
self.web_event.set()
|
||||
|
||||
# exit thread
|
||||
if self.close_thread_switch:
|
||||
self.channel_manager.clean_channel_resource_by_name(
|
||||
self.channel_name)
|
||||
logging.info('Stop thread:%s.', (self.thread_name))
|
||||
break
|
||||
@@ -0,0 +1,291 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
"""presenter channel manager module"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
|
||||
# max support 10 channels
|
||||
MAX_CHANNEL_NUM = 10
|
||||
|
||||
# when a channel have receive data,
|
||||
# the active status will last 3 seconds
|
||||
ACTIVE_LAST_TIME = 3
|
||||
|
||||
class ChannelResource():
|
||||
"""every channel has a ChannelResource object, contains a ChannelHandler object
|
||||
and a socket fileno. it corresponding to the ChannelFd one by one
|
||||
"""
|
||||
def __init__(self, handler, socket=None):
|
||||
self.handler = handler
|
||||
self.socket = socket
|
||||
|
||||
class ChannelFd():
|
||||
"""every channel has a ChannelFd object, contains a ChannelHandler
|
||||
object and channel name. It corresponds to the ChannelResource one by one
|
||||
"""
|
||||
def __init__(self, channel_name, handler):
|
||||
self.channel_name = channel_name
|
||||
self.handler = handler
|
||||
|
||||
class Channel():
|
||||
"""record user register channels
|
||||
self.image: if channel type is image, save the image here
|
||||
"""
|
||||
def __init__(self, channel_name):
|
||||
self.channel_name = channel_name
|
||||
self.image = None
|
||||
self.rectangle_list = None
|
||||
|
||||
class ChannelManager():
|
||||
"""manage all the api about channel
|
||||
__instance: ensure it is a single instance
|
||||
_channel_resources: a dict
|
||||
key: channel name
|
||||
value: a ChannelResource() object.
|
||||
_channel_fds: a dict
|
||||
key: socket fileno
|
||||
value: a ChannelFd() object.
|
||||
_channel_list: a list, member is a Channel() object."""
|
||||
|
||||
__instance = None
|
||||
channel_resources = {}
|
||||
channel_fds = {}
|
||||
channel_list = []
|
||||
channel_resource_lock = threading.Lock()
|
||||
channel_fds_lock = threading.Lock()
|
||||
channel_lock = threading.Lock()
|
||||
err_code_ok = 0
|
||||
err_code_too_many_channel = 1
|
||||
err_code_repeat_channel = 2
|
||||
|
||||
def __init__(self, channel_list=None):
|
||||
"""init func"""
|
||||
|
||||
def __new__(cls, channel_list=None):
|
||||
"""ensure only a single instance created. """
|
||||
if cls.__instance is None:
|
||||
cls.__instance = object.__new__(cls)
|
||||
# default create 2 channels: image and video
|
||||
# if channel_list is not None and isinstance(channel_list, list):
|
||||
# for i in channel_list:
|
||||
# cls.channel_list.append(Channel(channel_name=i))
|
||||
# logging.info("register channel %s", i)
|
||||
return cls.__instance
|
||||
|
||||
def _register_channel_fd(self, sock_fileno, channel_name):
|
||||
"""Internal func, create a ChannelFd object"""
|
||||
if self.channel_fds.get(sock_fileno):
|
||||
del self.channel_fds[sock_fileno]
|
||||
handler = self.channel_resources[channel_name].handler
|
||||
self.channel_fds[sock_fileno] = ChannelFd(channel_name, handler)
|
||||
|
||||
|
||||
def create_channel_resource(self, channel_name,
|
||||
channel_fd,
|
||||
media_type,
|
||||
handler):
|
||||
"""create a ChannelResource object which contains all the resources
|
||||
binding a channel.
|
||||
channel_name: channel name.
|
||||
channel_fd: socket fileno binding the channel.
|
||||
media_type: support image or video.
|
||||
handler: an channel handler process image data.
|
||||
"""
|
||||
with self.channel_resource_lock:
|
||||
log_info = "create channel resource,"
|
||||
log_info += " channel_name:%s, channel_fd:%u, media_type:%s"
|
||||
logging.info(log_info, channel_name, channel_fd, media_type)
|
||||
self.channel_resources[channel_name] = \
|
||||
ChannelResource(handler=handler, socket=channel_fd)
|
||||
self._register_channel_fd(channel_fd, channel_name)
|
||||
|
||||
def _clean_channel_resource(self, channel_name):
|
||||
"""Internal func, clean channel resource by channel name"""
|
||||
if self.channel_resources.get(channel_name):
|
||||
self.channel_resources[channel_name].handler.close_thread()
|
||||
self.channel_resources[channel_name].handler.web_event.set()
|
||||
self.channel_resources[channel_name].handler.image_event.set()
|
||||
del self.channel_resources[channel_name]
|
||||
logging.info("clean channel: %s's resource", channel_name)
|
||||
|
||||
def clean_channel_resource_by_fd(self, sock_fileno):
|
||||
"""
|
||||
clean channel resource by socket fileno
|
||||
sock_fileno: socket fileno which binding to an channel
|
||||
"""
|
||||
with self.channel_fds_lock:
|
||||
with self.channel_resource_lock:
|
||||
if self.channel_fds.get(sock_fileno):
|
||||
self._clean_channel_resource(
|
||||
self.channel_fds[sock_fileno].channel_name)
|
||||
del self.channel_fds[sock_fileno]
|
||||
|
||||
def clean_channel_resource_by_name(self, channel_name):
|
||||
"""clean channel resource by channel_name
|
||||
channel_name: channel name"""
|
||||
if self.channel_resources.get(channel_name):
|
||||
self.clean_channel_resource_by_fd(
|
||||
self.channel_resources[channel_name].socket)
|
||||
|
||||
def get_channel_handler_by_fd(self, sock_fileno):
|
||||
"""get channel handler by socket fileno"""
|
||||
with self.channel_fds_lock:
|
||||
if self.channel_fds.get(sock_fileno):
|
||||
return self.channel_fds[sock_fileno].handler
|
||||
return None
|
||||
|
||||
def is_channel_busy(self, channel_name):
|
||||
"""check if channel is busy """
|
||||
with self.channel_resource_lock:
|
||||
if self.channel_resources.get(channel_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def close_all_thread(self):
|
||||
"""if a channel process video type, it will create a thread.
|
||||
this func can close the thread.
|
||||
"""
|
||||
with self.channel_resource_lock:
|
||||
for channel_name in self.channel_resources:
|
||||
self.channel_resources[channel_name].handler.close_thread()
|
||||
|
||||
def get_channel_handler_by_name(self, channel_name):
|
||||
"""
|
||||
get the channel handlerby channel name
|
||||
"""
|
||||
with self.channel_resource_lock:
|
||||
if self.channel_resources.get(channel_name):
|
||||
return self.channel_resources[channel_name].handler
|
||||
return None
|
||||
|
||||
def list_channels(self):
|
||||
"""
|
||||
return all the channel name and the status
|
||||
status is indicating active state or not
|
||||
"""
|
||||
with self.channel_lock:
|
||||
return [{'status': self.is_channel_busy(i.channel_name),
|
||||
'name': i.channel_name} for i in self.channel_list]
|
||||
|
||||
def register_one_channel(self, channel_name):
|
||||
"""
|
||||
register a channel path, user create a channel via browser
|
||||
"""
|
||||
with self.channel_lock:
|
||||
if len(self.channel_list) >= MAX_CHANNEL_NUM:
|
||||
logging.info("register channel: %s fail, \
|
||||
exceed max number 10.", channel_name)
|
||||
return self.err_code_too_many_channel
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
logging.info("register channel: %s fail, \
|
||||
already exist.", channel_name)
|
||||
return self.err_code_repeat_channel
|
||||
|
||||
self.channel_list.append(Channel(channel_name=channel_name))
|
||||
logging.info("register channel: %s", channel_name)
|
||||
return self.err_code_ok
|
||||
|
||||
def unregister_one_channel(self, channel_name):
|
||||
"""
|
||||
unregister a channel path, user delete a channel via browser
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
self.clean_channel_resource_by_name(channel_name)
|
||||
logging.info("unregister channel: %s", channel_name)
|
||||
del self.channel_list[i]
|
||||
break
|
||||
|
||||
def is_channel_exist(self, channel_name):
|
||||
"""
|
||||
Check if a channel is exist
|
||||
True: exist
|
||||
False: not exist
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def save_channel_image(self, channel_name, image_data, rectangle_list):
|
||||
"""
|
||||
when a channel bounding to image type,
|
||||
server will permanent hold an image for it.
|
||||
this func save a image in memory
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
self.channel_list[i].image = image_data
|
||||
self.channel_list[i].rectangle_list = rectangle_list
|
||||
break
|
||||
|
||||
def get_channel_image(self, channel_name):
|
||||
"""
|
||||
when a channel bounding to image type,
|
||||
server will permanent hold an image for it.
|
||||
this func get the image
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
return self.channel_list[i].image
|
||||
|
||||
# channel not exist
|
||||
return None
|
||||
|
||||
def get_channel_image_with_rectangle(self, channel_name):
|
||||
"""
|
||||
A new method for display server,
|
||||
return the image and rectangle list
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
return (self.channel_list[i].image, self.channel_list[i].rectangle_list)
|
||||
return (None, None)
|
||||
|
||||
def clean_channel_image(self, channel_name):
|
||||
"""
|
||||
when a channel bounding to image type,
|
||||
server will permanent hold an image for it.
|
||||
this func clean the image
|
||||
"""
|
||||
with self.channel_lock:
|
||||
for i in range(len(self.channel_list)):
|
||||
if self.channel_list[i].channel_name == channel_name:
|
||||
self.channel_list[i].image = None
|
||||
break
|
||||
@@ -0,0 +1,98 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
"""Parameter Validation module"""
|
||||
import logging
|
||||
|
||||
PORT_INTERVAL_BEGIN = 1024
|
||||
PORT_INTERVAL_END = 49151
|
||||
|
||||
def validate_ip(ip_str):
|
||||
if ip_str == '0.0.0.0':
|
||||
logging.error("IP Addr \"0.0.0.0\" is illegal")
|
||||
print("IP Addr \"0.0.0.0\" is illegal")
|
||||
return False
|
||||
|
||||
sep = ip_str.split('.')
|
||||
if len(sep) != 4:
|
||||
return False
|
||||
for i, x in enumerate(sep):
|
||||
try:
|
||||
int_x = int(x)
|
||||
if int_x < 0 or int_x > 255:
|
||||
logging.error("Illegal ip: %s", ip_str)
|
||||
print("Illegal ip: %s"%ip_str)
|
||||
return False
|
||||
except ValueError:
|
||||
logging.error("IP format error:%s", ip_str)
|
||||
print("IP format error:%s"%ip_str)
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate_port(value_str):
|
||||
try:
|
||||
value = int(value_str)
|
||||
if value < PORT_INTERVAL_BEGIN or value > PORT_INTERVAL_END:
|
||||
logging.error("Illegal port: %d", value)
|
||||
print("Illegal port: %d"%value)
|
||||
return False
|
||||
except ValueError:
|
||||
logging.error("Port format error:%s", value_str)
|
||||
print("Port format error:%s"%value_str)
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate_integer(value_str, begin, end):
|
||||
try:
|
||||
value = int(value_str)
|
||||
if value < begin or value > end:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def Integer_greater(value_str, compared_value):
|
||||
try:
|
||||
value = int(value_str)
|
||||
if value < compared_value:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate_float(value_str, begin, end):
|
||||
try:
|
||||
value = float(value_str)
|
||||
if value < begin or value > end:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,525 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: presenter_message.proto
|
||||
|
||||
import sys
|
||||
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='presenter_message.proto',
|
||||
package='ascend.presenter.proto',
|
||||
syntax='proto3',
|
||||
serialized_pb=_b('\n\x17presenter_message.proto\x12\x16\x61scend.presenter.proto\"l\n\x12OpenChannelRequest\x12\x14\n\x0c\x63hannel_name\x18\x01 \x01(\t\x12@\n\x0c\x63ontent_type\x18\x02 \x01(\x0e\x32*.ascend.presenter.proto.ChannelContentType\"n\n\x13OpenChannelResponse\x12@\n\nerror_code\x18\x01 \x01(\x0e\x32,.ascend.presenter.proto.OpenChannelErrorCode\x12\x15\n\rerror_message\x18\x02 \x01(\t\"\x12\n\x10HeartbeatMessage\"\"\n\nCoordinate\x12\t\n\x01x\x18\x01 \x01(\r\x12\t\n\x01y\x18\x02 \x01(\r\"\x94\x01\n\x0eRectangle_Attr\x12\x34\n\x08left_top\x18\x01 \x01(\x0b\x32\".ascend.presenter.proto.Coordinate\x12\x38\n\x0cright_bottom\x18\x02 \x01(\x0b\x32\".ascend.presenter.proto.Coordinate\x12\x12\n\nlabel_text\x18\x03 \x01(\t\"\xb7\x01\n\x13PresentImageRequest\x12\x33\n\x06\x66ormat\x18\x01 \x01(\x0e\x32#.ascend.presenter.proto.ImageFormat\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12>\n\x0erectangle_list\x18\x05 \x03(\x0b\x32&.ascend.presenter.proto.Rectangle_Attr\"o\n\x14PresentImageResponse\x12@\n\nerror_code\x18\x01 \x01(\x0e\x32,.ascend.presenter.proto.PresentDataErrorCode\x12\x15\n\rerror_message\x18\x02 \x01(\t*\xa5\x01\n\x14OpenChannelErrorCode\x12\x19\n\x15kOpenChannelErrorNone\x10\x00\x12\"\n\x1ekOpenChannelErrorNoSuchChannel\x10\x01\x12)\n%kOpenChannelErrorChannelAlreadyOpened\x10\x02\x12#\n\x16kOpenChannelErrorOther\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01*P\n\x12\x43hannelContentType\x12\x1c\n\x18kChannelContentTypeImage\x10\x00\x12\x1c\n\x18kChannelContentTypeVideo\x10\x01*#\n\x0bImageFormat\x12\x14\n\x10kImageFormatJpeg\x10\x00*\xa4\x01\n\x14PresentDataErrorCode\x12\x19\n\x15kPresentDataErrorNone\x10\x00\x12$\n kPresentDataErrorUnsupportedType\x10\x01\x12&\n\"kPresentDataErrorUnsupportedFormat\x10\x02\x12#\n\x16kPresentDataErrorOther\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x62\x06proto3')
|
||||
)
|
||||
|
||||
_OPENCHANNELERRORCODE = _descriptor.EnumDescriptor(
|
||||
name='OpenChannelErrorCode',
|
||||
full_name='ascend.presenter.proto.OpenChannelErrorCode',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kOpenChannelErrorNone', index=0, number=0,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kOpenChannelErrorNoSuchChannel', index=1, number=1,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kOpenChannelErrorChannelAlreadyOpened', index=2, number=2,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kOpenChannelErrorOther', index=3, number=-1,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=780,
|
||||
serialized_end=945,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_OPENCHANNELERRORCODE)
|
||||
|
||||
OpenChannelErrorCode = enum_type_wrapper.EnumTypeWrapper(_OPENCHANNELERRORCODE)
|
||||
_CHANNELCONTENTTYPE = _descriptor.EnumDescriptor(
|
||||
name='ChannelContentType',
|
||||
full_name='ascend.presenter.proto.ChannelContentType',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kChannelContentTypeImage', index=0, number=0,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kChannelContentTypeVideo', index=1, number=1,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=947,
|
||||
serialized_end=1027,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_CHANNELCONTENTTYPE)
|
||||
|
||||
ChannelContentType = enum_type_wrapper.EnumTypeWrapper(_CHANNELCONTENTTYPE)
|
||||
_IMAGEFORMAT = _descriptor.EnumDescriptor(
|
||||
name='ImageFormat',
|
||||
full_name='ascend.presenter.proto.ImageFormat',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kImageFormatJpeg', index=0, number=0,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=1029,
|
||||
serialized_end=1064,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_IMAGEFORMAT)
|
||||
|
||||
ImageFormat = enum_type_wrapper.EnumTypeWrapper(_IMAGEFORMAT)
|
||||
_PRESENTDATAERRORCODE = _descriptor.EnumDescriptor(
|
||||
name='PresentDataErrorCode',
|
||||
full_name='ascend.presenter.proto.PresentDataErrorCode',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kPresentDataErrorNone', index=0, number=0,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kPresentDataErrorUnsupportedType', index=1, number=1,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kPresentDataErrorUnsupportedFormat', index=2, number=2,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='kPresentDataErrorOther', index=3, number=-1,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=1067,
|
||||
serialized_end=1231,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_PRESENTDATAERRORCODE)
|
||||
|
||||
PresentDataErrorCode = enum_type_wrapper.EnumTypeWrapper(_PRESENTDATAERRORCODE)
|
||||
kOpenChannelErrorNone = 0
|
||||
kOpenChannelErrorNoSuchChannel = 1
|
||||
kOpenChannelErrorChannelAlreadyOpened = 2
|
||||
kOpenChannelErrorOther = -1
|
||||
kChannelContentTypeImage = 0
|
||||
kChannelContentTypeVideo = 1
|
||||
kImageFormatJpeg = 0
|
||||
kPresentDataErrorNone = 0
|
||||
kPresentDataErrorUnsupportedType = 1
|
||||
kPresentDataErrorUnsupportedFormat = 2
|
||||
kPresentDataErrorOther = -1
|
||||
|
||||
|
||||
|
||||
_OPENCHANNELREQUEST = _descriptor.Descriptor(
|
||||
name='OpenChannelRequest',
|
||||
full_name='ascend.presenter.proto.OpenChannelRequest',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='channel_name', full_name='ascend.presenter.proto.OpenChannelRequest.channel_name', index=0,
|
||||
number=1, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='content_type', full_name='ascend.presenter.proto.OpenChannelRequest.content_type', index=1,
|
||||
number=2, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=51,
|
||||
serialized_end=159,
|
||||
)
|
||||
|
||||
|
||||
_OPENCHANNELRESPONSE = _descriptor.Descriptor(
|
||||
name='OpenChannelResponse',
|
||||
full_name='ascend.presenter.proto.OpenChannelResponse',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='error_code', full_name='ascend.presenter.proto.OpenChannelResponse.error_code', index=0,
|
||||
number=1, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='error_message', full_name='ascend.presenter.proto.OpenChannelResponse.error_message', index=1,
|
||||
number=2, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=161,
|
||||
serialized_end=271,
|
||||
)
|
||||
|
||||
|
||||
_HEARTBEATMESSAGE = _descriptor.Descriptor(
|
||||
name='HeartbeatMessage',
|
||||
full_name='ascend.presenter.proto.HeartbeatMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=273,
|
||||
serialized_end=291,
|
||||
)
|
||||
|
||||
|
||||
_COORDINATE = _descriptor.Descriptor(
|
||||
name='Coordinate',
|
||||
full_name='ascend.presenter.proto.Coordinate',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='x', full_name='ascend.presenter.proto.Coordinate.x', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='y', full_name='ascend.presenter.proto.Coordinate.y', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=293,
|
||||
serialized_end=327,
|
||||
)
|
||||
|
||||
|
||||
_RECTANGLE_ATTR = _descriptor.Descriptor(
|
||||
name='Rectangle_Attr',
|
||||
full_name='ascend.presenter.proto.Rectangle_Attr',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='left_top', full_name='ascend.presenter.proto.Rectangle_Attr.left_top', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='right_bottom', full_name='ascend.presenter.proto.Rectangle_Attr.right_bottom', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='label_text', full_name='ascend.presenter.proto.Rectangle_Attr.label_text', index=2,
|
||||
number=3, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=330,
|
||||
serialized_end=478,
|
||||
)
|
||||
|
||||
|
||||
_PRESENTIMAGEREQUEST = _descriptor.Descriptor(
|
||||
name='PresentImageRequest',
|
||||
full_name='ascend.presenter.proto.PresentImageRequest',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='format', full_name='ascend.presenter.proto.PresentImageRequest.format', index=0,
|
||||
number=1, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='width', full_name='ascend.presenter.proto.PresentImageRequest.width', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='height', full_name='ascend.presenter.proto.PresentImageRequest.height', index=2,
|
||||
number=3, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='data', full_name='ascend.presenter.proto.PresentImageRequest.data', index=3,
|
||||
number=4, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b(""),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rectangle_list', full_name='ascend.presenter.proto.PresentImageRequest.rectangle_list', index=4,
|
||||
number=5, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=481,
|
||||
serialized_end=664,
|
||||
)
|
||||
|
||||
|
||||
_PRESENTIMAGERESPONSE = _descriptor.Descriptor(
|
||||
name='PresentImageResponse',
|
||||
full_name='ascend.presenter.proto.PresentImageResponse',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='error_code', full_name='ascend.presenter.proto.PresentImageResponse.error_code', index=0,
|
||||
number=1, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='error_message', full_name='ascend.presenter.proto.PresentImageResponse.error_message', index=1,
|
||||
number=2, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=666,
|
||||
serialized_end=777,
|
||||
)
|
||||
|
||||
_OPENCHANNELREQUEST.fields_by_name['content_type'].enum_type = _CHANNELCONTENTTYPE
|
||||
_OPENCHANNELRESPONSE.fields_by_name['error_code'].enum_type = _OPENCHANNELERRORCODE
|
||||
_RECTANGLE_ATTR.fields_by_name['left_top'].message_type = _COORDINATE
|
||||
_RECTANGLE_ATTR.fields_by_name['right_bottom'].message_type = _COORDINATE
|
||||
_PRESENTIMAGEREQUEST.fields_by_name['format'].enum_type = _IMAGEFORMAT
|
||||
_PRESENTIMAGEREQUEST.fields_by_name['rectangle_list'].message_type = _RECTANGLE_ATTR
|
||||
_PRESENTIMAGERESPONSE.fields_by_name['error_code'].enum_type = _PRESENTDATAERRORCODE
|
||||
DESCRIPTOR.message_types_by_name['OpenChannelRequest'] = _OPENCHANNELREQUEST
|
||||
DESCRIPTOR.message_types_by_name['OpenChannelResponse'] = _OPENCHANNELRESPONSE
|
||||
DESCRIPTOR.message_types_by_name['HeartbeatMessage'] = _HEARTBEATMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['Coordinate'] = _COORDINATE
|
||||
DESCRIPTOR.message_types_by_name['Rectangle_Attr'] = _RECTANGLE_ATTR
|
||||
DESCRIPTOR.message_types_by_name['PresentImageRequest'] = _PRESENTIMAGEREQUEST
|
||||
DESCRIPTOR.message_types_by_name['PresentImageResponse'] = _PRESENTIMAGERESPONSE
|
||||
DESCRIPTOR.enum_types_by_name['OpenChannelErrorCode'] = _OPENCHANNELERRORCODE
|
||||
DESCRIPTOR.enum_types_by_name['ChannelContentType'] = _CHANNELCONTENTTYPE
|
||||
DESCRIPTOR.enum_types_by_name['ImageFormat'] = _IMAGEFORMAT
|
||||
DESCRIPTOR.enum_types_by_name['PresentDataErrorCode'] = _PRESENTDATAERRORCODE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
OpenChannelRequest = _reflection.GeneratedProtocolMessageType('OpenChannelRequest', (_message.Message,), dict(
|
||||
DESCRIPTOR = _OPENCHANNELREQUEST,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.OpenChannelRequest)
|
||||
))
|
||||
_sym_db.RegisterMessage(OpenChannelRequest)
|
||||
|
||||
OpenChannelResponse = _reflection.GeneratedProtocolMessageType('OpenChannelResponse', (_message.Message,), dict(
|
||||
DESCRIPTOR = _OPENCHANNELRESPONSE,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.OpenChannelResponse)
|
||||
))
|
||||
_sym_db.RegisterMessage(OpenChannelResponse)
|
||||
|
||||
HeartbeatMessage = _reflection.GeneratedProtocolMessageType('HeartbeatMessage', (_message.Message,), dict(
|
||||
DESCRIPTOR = _HEARTBEATMESSAGE,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.HeartbeatMessage)
|
||||
))
|
||||
_sym_db.RegisterMessage(HeartbeatMessage)
|
||||
|
||||
Coordinate = _reflection.GeneratedProtocolMessageType('Coordinate', (_message.Message,), dict(
|
||||
DESCRIPTOR = _COORDINATE,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.Coordinate)
|
||||
))
|
||||
_sym_db.RegisterMessage(Coordinate)
|
||||
|
||||
Rectangle_Attr = _reflection.GeneratedProtocolMessageType('Rectangle_Attr', (_message.Message,), dict(
|
||||
DESCRIPTOR = _RECTANGLE_ATTR,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.Rectangle_Attr)
|
||||
))
|
||||
_sym_db.RegisterMessage(Rectangle_Attr)
|
||||
|
||||
PresentImageRequest = _reflection.GeneratedProtocolMessageType('PresentImageRequest', (_message.Message,), dict(
|
||||
DESCRIPTOR = _PRESENTIMAGEREQUEST,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.PresentImageRequest)
|
||||
))
|
||||
_sym_db.RegisterMessage(PresentImageRequest)
|
||||
|
||||
PresentImageResponse = _reflection.GeneratedProtocolMessageType('PresentImageResponse', (_message.Message,), dict(
|
||||
DESCRIPTOR = _PRESENTIMAGERESPONSE,
|
||||
__module__ = 'presenter_message_pb2'
|
||||
# @@protoc_insertion_point(class_scope:ascend.presenter.proto.PresentImageResponse)
|
||||
))
|
||||
_sym_db.RegisterMessage(PresentImageResponse)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -0,0 +1,463 @@
|
||||
# =======================================================================
|
||||
#
|
||||
# Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1 Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2 Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3 Neither the names of the copyright holders nor the names of the
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# =======================================================================
|
||||
#
|
||||
"""presenter socket server module"""
|
||||
|
||||
import threading
|
||||
import select
|
||||
import struct
|
||||
import logging
|
||||
import socket
|
||||
from google.protobuf.message import DecodeError
|
||||
import common.presenter_message_pb2 as pb2
|
||||
from common.channel_manager import ChannelManager
|
||||
from common.channel_handler import ChannelHandler
|
||||
|
||||
#read nothing from socket.recv()
|
||||
SOCK_RECV_NULL = b''
|
||||
|
||||
# epool will return if no event coming in 1 s
|
||||
EPOLL_TIMEOUT = 1
|
||||
|
||||
# it specifies the number of unaccepted connections that
|
||||
# the system will allow before refusing new connections.
|
||||
SOCKET_WAIT_QUEUE = 2
|
||||
|
||||
# message head length, include 4 bytes message total length
|
||||
# and 1 byte message name length
|
||||
MSG_HEAD_LENGTH = 5
|
||||
|
||||
|
||||
class PresenterSocketServer():
|
||||
"""a socket server communication with presenter agent.
|
||||
|
||||
"""
|
||||
def __init__(self, server_address):
|
||||
"""
|
||||
Args:
|
||||
server_address: server listen address,
|
||||
include an ipv4 address and a port.
|
||||
"""
|
||||
|
||||
# thread exit switch, if set true, thread must exit immediately.
|
||||
self.thread_exit_switch = False
|
||||
# message head length, include 4 bytes message total length
|
||||
# and 1 byte message name length
|
||||
self.msg_head_len = 5
|
||||
self._create_socket_server(server_address)
|
||||
|
||||
def _create_socket_server(self, server_address):
|
||||
"""
|
||||
create a socket server
|
||||
Args:
|
||||
server_address: server listen address,
|
||||
include an ipv4 address and a port.
|
||||
"""
|
||||
|
||||
# Create a socket server.
|
||||
self._sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self._sock_server.bind(server_address)
|
||||
self._sock_server.listen(SOCKET_WAIT_QUEUE)
|
||||
self._sock_server.setblocking(False)
|
||||
|
||||
# Get server host name and port
|
||||
host, port = self._sock_server.getsockname()[:2]
|
||||
|
||||
# Start presenter socket server thread.
|
||||
threading.Thread(target=self._server_listen_thread).start()
|
||||
|
||||
# Display directly on the screen
|
||||
print('Presenter socket server listen on %s:%s\n' % (host, port))
|
||||
|
||||
def set_exit_switch(self):
|
||||
"""set switch True to stop presenter socket server thread."""
|
||||
self.thread_exit_switch = True
|
||||
|
||||
def _read_socket(self, conn, read_len):
|
||||
'''
|
||||
Read fixed length data
|
||||
Args:
|
||||
conn: a socket connection
|
||||
read_len: read fix byte.
|
||||
Returns:
|
||||
ret: True or False
|
||||
buf: read fix byte buf.
|
||||
'''
|
||||
has_read_len = 0
|
||||
read_buf = SOCK_RECV_NULL
|
||||
total_buf = SOCK_RECV_NULL
|
||||
while has_read_len != read_len:
|
||||
try:
|
||||
read_buf = conn.recv(read_len - has_read_len)
|
||||
except socket.error:
|
||||
logging.error("socket %u exception:socket.error", conn.fileno())
|
||||
return False, None
|
||||
if read_buf == SOCK_RECV_NULL:
|
||||
return False, None
|
||||
total_buf += read_buf
|
||||
has_read_len = len(total_buf)
|
||||
|
||||
return True, total_buf
|
||||
|
||||
def _read_msg_head(self, sock_fileno, conns):
|
||||
'''
|
||||
Args:
|
||||
sock_fileno: a socket fileno
|
||||
conns: all socket connections which created by server.
|
||||
Returns:
|
||||
msg_total_len: total message length.
|
||||
msg_name_len: message name length.
|
||||
'''
|
||||
ret, msg_head = self._read_socket(conns[sock_fileno], self.msg_head_len)
|
||||
if not ret:
|
||||
logging.error("socket %u receive msg head null", sock_fileno)
|
||||
return None, None
|
||||
|
||||
# in Struct(), 'I' is unsigned int, 'B' is unsigned char
|
||||
msg_head_data = struct.Struct('IB')
|
||||
(msg_total_len, msg_name_len) = msg_head_data.unpack(msg_head)
|
||||
msg_total_len = socket.ntohl(msg_total_len)
|
||||
|
||||
return msg_total_len, msg_name_len
|
||||
|
||||
def _read_msg_name(self, sock_fd, conns, msg_name_len):
|
||||
'''
|
||||
Args:
|
||||
sock_fd: a socket fileno
|
||||
conns: all socket connections which created by server.
|
||||
msg_name_len: message name length.
|
||||
Returns:
|
||||
ret: True or False
|
||||
msg_name: message name.
|
||||
'''
|
||||
ret, msg_name = self._read_socket(conns[sock_fd], msg_name_len)
|
||||
if not ret:
|
||||
logging.error("socket %u receive msg name null", sock_fd)
|
||||
return False, None
|
||||
try:
|
||||
msg_name = msg_name.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
logging.error("msg name decode to utf-8 error")
|
||||
return False, None
|
||||
|
||||
return True, msg_name
|
||||
|
||||
def _read_msg_body(self, sock_fd, conns, msg_body_len, msgs):
|
||||
'''
|
||||
Args:
|
||||
sock_fd: a socket fileno
|
||||
conns: all socket connections which created by server.
|
||||
msg_name_len: message name length.
|
||||
msgs: msg read from a socket
|
||||
Returns:
|
||||
ret: True or False
|
||||
'''
|
||||
ret, msg_body = self._read_socket(conns[sock_fd], msg_body_len)
|
||||
if not ret:
|
||||
logging.error("socket %u receive msg body null", sock_fd)
|
||||
return False
|
||||
msgs[sock_fd] = msg_body
|
||||
return True
|
||||
|
||||
def _read_sock_and_process_msg(self, sock_fileno, conns, msgs):
|
||||
'''
|
||||
Args:
|
||||
sock_fileno: a socket fileno, return value of socket.fileno()
|
||||
conns: all socket connections registered in epoll
|
||||
msgs: msg read from a socket
|
||||
Returns:
|
||||
ret: True or False
|
||||
'''
|
||||
|
||||
# Step1: read msg head
|
||||
msg_total_len, msg_name_len = self._read_msg_head(sock_fileno, conns)
|
||||
if msg_total_len is None:
|
||||
logging.error("msg_total_len is None.")
|
||||
return False
|
||||
|
||||
# Step2: read msg name
|
||||
ret, msg_name = self._read_msg_name(sock_fileno, conns, msg_name_len)
|
||||
if not ret:
|
||||
return ret
|
||||
|
||||
# Step3: read msg body
|
||||
msg_body_len = msg_total_len - self.msg_head_len - msg_name_len
|
||||
if msg_body_len < 0:
|
||||
logging.error("msg_total_len:%u, msg_name_len:%u, msg_body_len:%u",
|
||||
msg_total_len, msg_name_len, msg_body_len)
|
||||
return False
|
||||
ret = self._read_msg_body(sock_fileno, conns, msg_body_len, msgs)
|
||||
if not ret:
|
||||
return ret
|
||||
|
||||
# Step4: process msg
|
||||
ret = self._process_msg(conns[sock_fileno], msg_name, msgs[sock_fileno])
|
||||
return ret
|
||||
|
||||
def _process_epollin(self, sock_fileno, epoll, conns, msgs):
|
||||
'''
|
||||
Args:
|
||||
sock_fileno: a socket fileno, return value of socket.fileno()
|
||||
epoll: a set of select.epoll.
|
||||
conns: all socket connections registered in epoll
|
||||
msgs: msg read from a socket
|
||||
'''
|
||||
msgs[sock_fileno] = b''
|
||||
try:
|
||||
ret = self._read_sock_and_process_msg(sock_fileno, conns, msgs)
|
||||
if not ret:
|
||||
self._clean_connect(sock_fileno, epoll, conns, msgs)
|
||||
except socket.error:
|
||||
logging.error("receive socket error.")
|
||||
self._clean_connect(sock_fileno, epoll, conns, msgs)
|
||||
|
||||
def _accept_new_socket(self, epoll, conns):
|
||||
'''
|
||||
Args:
|
||||
epoll: a set of select.epoll.
|
||||
conns: all socket connections registered in epoll
|
||||
'''
|
||||
try:
|
||||
new_conn, address = self._sock_server.accept()
|
||||
new_conn.setblocking(True)
|
||||
epoll.register(new_conn.fileno(), select.EPOLLIN | select.EPOLLHUP)
|
||||
conns[new_conn.fileno()] = new_conn
|
||||
logging.info("create new connection:client-ip:%s, client-port:%s, fd:%s",
|
||||
address[0], address[1], new_conn.fileno())
|
||||
except socket.error:
|
||||
logging.error("socket.error exception when sock.accept()")
|
||||
|
||||
def _server_listen_thread(self):
|
||||
"""socket server thread, epoll listening all the socket events"""
|
||||
epoll = select.epoll()
|
||||
epoll.register(self._sock_server.fileno(), select.EPOLLIN | select.EPOLLHUP)
|
||||
try:
|
||||
conns = {}
|
||||
msgs = {}
|
||||
while True:
|
||||
# thread must exit immediately
|
||||
if self.thread_exit_switch:
|
||||
break
|
||||
|
||||
events = epoll.poll(EPOLL_TIMEOUT)
|
||||
# timeout, but no event come, continue waiting
|
||||
if not events:
|
||||
continue
|
||||
|
||||
for sock_fileno, event in events:
|
||||
# new connection request from presenter agent
|
||||
if self._sock_server.fileno() == sock_fileno:
|
||||
self._accept_new_socket(epoll, conns)
|
||||
|
||||
# remote connection closed
|
||||
# it means presenter agent exit withot close socket.
|
||||
elif event & select.EPOLLHUP:
|
||||
logging.info("receive event EPOLLHUP")
|
||||
self._clean_connect(sock_fileno, epoll, conns, msgs)
|
||||
# new data coming in a socket connection
|
||||
elif event & select.EPOLLIN:
|
||||
self._process_epollin(sock_fileno, epoll, conns, msgs)
|
||||
# receive event not recognize
|
||||
else:
|
||||
logging.error("not recognize event %f", event)
|
||||
self._clean_connect(sock_fileno, epoll, conns, msgs)
|
||||
|
||||
finally:
|
||||
logging.info("conns:%s", conns)
|
||||
logging.info("presenter server listen thread exit.")
|
||||
epoll.unregister(self._sock_server.fileno())
|
||||
epoll.close()
|
||||
self._sock_server.close()
|
||||
|
||||
|
||||
def _process_heartbeat(self, conn):
|
||||
'''
|
||||
set heartbeat
|
||||
Args:
|
||||
conn: a socket connection
|
||||
Returns:
|
||||
True: set heartbeat ok.
|
||||
|
||||
'''
|
||||
sock_fileno = conn.fileno()
|
||||
handler = self.channel_manager.get_channel_handler_by_fd(sock_fileno)
|
||||
if handler is not None:
|
||||
handler.set_heartbeat()
|
||||
|
||||
return True
|
||||
|
||||
def _process_open_channel(self, conn, msg_data):
|
||||
"""
|
||||
Deserialization protobuf and process open_channel request
|
||||
Args:
|
||||
conn: a socket connection
|
||||
msg_data: a protobuf struct, include open channel request.
|
||||
|
||||
Returns:
|
||||
|
||||
protobuf structure like this:
|
||||
----------------------------------------------
|
||||
|channel_name | string |
|
||||
|----------------------------------------------
|
||||
|content_type | ChannelContentType |
|
||||
|----------------------------------------------
|
||||
|
||||
enum ChannelContentType {
|
||||
kChannelContentTypeImage = 0;
|
||||
kChannelContentTypeVideo = 1;
|
||||
}
|
||||
"""
|
||||
request = pb2.OpenChannelRequest()
|
||||
response = pb2.OpenChannelResponse()
|
||||
|
||||
try:
|
||||
request.ParseFromString(msg_data)
|
||||
except DecodeError:
|
||||
logging.error("ParseFromString exception: Error parsing message")
|
||||
channel_name = "unknown channel"
|
||||
return self._response_open_channel(conn, channel_name, response,
|
||||
pb2.kOpenChannelErrorOther)
|
||||
|
||||
channel_name = request.channel_name
|
||||
|
||||
# check channel name if exist
|
||||
if not self.channel_manager.is_channel_exist(channel_name):
|
||||
logging.error("channel name %s is not exist.", channel_name)
|
||||
# if channel is not exist, need to create the channel
|
||||
ret = self.channel_manager.register_one_channel(channel_name)
|
||||
if ret != ChannelManager.err_code_ok:
|
||||
logging.error("Create the channel %s failed!, and ret is %d", channel_name, ret)
|
||||
err_code = pb2.kOpenChannelErrorOther
|
||||
self._response_open_channel(conn, channel_name, response, err_code)
|
||||
|
||||
# check channel path if busy
|
||||
if self.channel_manager.is_channel_busy(channel_name):
|
||||
logging.error("channel path %s is busy.", channel_name)
|
||||
err_code = pb2.kOpenChannelErrorChannelAlreadyOpened
|
||||
return self._response_open_channel(conn, channel_name, response,
|
||||
err_code)
|
||||
|
||||
# if channel type is image, need clean image if exist
|
||||
self.channel_manager.clean_channel_image(channel_name)
|
||||
|
||||
if request.content_type == pb2.kChannelContentTypeImage:
|
||||
media_type = "image"
|
||||
elif request.content_type == pb2.kChannelContentTypeVideo:
|
||||
media_type = "video"
|
||||
else:
|
||||
logging.error("media type %s is not recognized.",
|
||||
request.content_type)
|
||||
return self._response_open_channel(conn, channel_name, response,
|
||||
pb2.kOpenChannelErrorOther)
|
||||
|
||||
handler = ChannelHandler(channel_name, media_type)
|
||||
self.channel_manager.create_channel_resource(
|
||||
channel_name, conn.fileno(), media_type, handler)
|
||||
|
||||
return self._response_open_channel(conn, channel_name, response,
|
||||
pb2.kOpenChannelErrorNone)
|
||||
|
||||
def _response_open_channel(self, conn, channel_name, response, err_code):
|
||||
"""
|
||||
Assemble protobuf to response open_channel request
|
||||
Args:
|
||||
conn: a socket connection
|
||||
channel_name: name of a channel.
|
||||
response: a protobuf response to presenter agent
|
||||
err_code: part of the response
|
||||
|
||||
Returns:
|
||||
ret_code:True or False
|
||||
|
||||
Message structure like this:
|
||||
--------------------------------------------------------------------
|
||||
|total message len | int | 4 bytes |
|
||||
|-------------------------------------------------------------------
|
||||
|message name len | byte | 1 byte |
|
||||
|-------------------------------------------------------------------
|
||||
|message name | string | xx bytes |
|
||||
|-------------------------------------------------------------------
|
||||
|message body | protobuf | xx bytes |
|
||||
--------------------------------------------------------------------
|
||||
|
||||
protobuf structure like this:
|
||||
--------------------------------------------------------------------
|
||||
|error_code | enum | OpenChannelErrorCode |
|
||||
|-------------------------------------------------------------------
|
||||
|error_message | string | xx bytes |
|
||||
|-------------------------------------------------------------------
|
||||
|
||||
enum OpenChannelErrorCode {
|
||||
kOpenChannelErrorNone = 0;
|
||||
kOpenChannelErrorNoSuchChannel = 1;
|
||||
kOpenChannelErrorChannelAlreadyOpened = 2;
|
||||
kOpenChannelErrorOther = -1;
|
||||
}
|
||||
"""
|
||||
response.error_code = err_code
|
||||
ret_code = False
|
||||
if err_code == pb2.kOpenChannelErrorNoSuchChannel:
|
||||
response.error_message = "channel {} not exist." \
|
||||
.format(channel_name)
|
||||
elif err_code == pb2.kOpenChannelErrorChannelAlreadyOpened:
|
||||
response.error_message = "channel {} is busy.".format(channel_name)
|
||||
elif err_code == pb2.kOpenChannelErrorNone:
|
||||
response.error_message = "open channel succeed"
|
||||
ret_code = True
|
||||
else:
|
||||
response.error_message = "Unknown err open channel {}." \
|
||||
.format(channel_name)
|
||||
|
||||
self.send_message(conn, response, pb2._OPENCHANNELRESPONSE.full_name)
|
||||
return ret_code
|
||||
|
||||
def send_message(self, conn, protobuf, msg_name):
|
||||
'''
|
||||
API for send message
|
||||
Args:
|
||||
conn: a socket connection.
|
||||
protobuf: message body defined in protobuf.
|
||||
msg_name: msg name.
|
||||
Returns: NA
|
||||
'''
|
||||
message_data = protobuf.SerializeToString()
|
||||
message_len = len(message_data)
|
||||
|
||||
msg_name_size = len(msg_name)
|
||||
msg_total_size = self.msg_head_len + msg_name_size + message_len
|
||||
# in Struct(), 'I' is unsigned int, 'B' is unsigned char
|
||||
s = struct.Struct('IB')
|
||||
msg_head = (socket.htonl(msg_total_size), msg_name_size)
|
||||
packed_msg_head = s.pack(*msg_head)
|
||||
msg_data = packed_msg_head + \
|
||||
bytes(msg_name, encoding="utf-8") + message_data
|
||||
conn.sendall(msg_data)
|
||||
Reference in New Issue
Block a user