This commit is contained in:
ascendhuawei
2020-09-16 11:50:53 -07:00
commit a61dda4612
97 changed files with 15410 additions and 0 deletions
View File
+209
View File
@@ -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))]
+227
View File
@@ -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
+291
View File
@@ -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)