EG version upgrade to 1.3
[ealt-edge.git] / example-apps / ROBO / retail_app / inventry / retail_app.py
1 #
2 # Copyright 2020 Huawei Technologies Co., Ltd.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17 import config
18 from flask_sslify import SSLify
19 from flask import Flask, request, jsonify, Response
20 from flask_cors import CORS
21 from influxdb import InfluxDBClient
22 import json
23 import requests
24 import os
25 import cv2
26 import os.path
27 from os import path
28 import base64
29 import time
30 import sys
31
32 app = Flask(__name__)
33 CORS(app)
34 sslify = SSLify(app)
35 app.config['JSON_AS_ASCII'] = False
36 app.config['UPLOAD_PATH'] = '/usr/app/images_result/'
37 app.config['VIDEO_PATH'] = '/usr/app/test/resources/'
38 app.config['supports_credentials'] = True
39 app.config['CORS_SUPPORTS_CREDENTIALS'] = True
40 app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
41 ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])
42 ALLOWED_VIDEO_EXTENSIONS = {'mp4'}
43 count = 0
44 listOfMsgs = []
45 listOfCameras = []
46 listOfVideos = []
47
48
49 class inventory_info:
50     """
51     Store the data and manage multiple input video feeds
52     """
53     def __init__(self, status="Needs Filling", time=0):
54         self.type = "Shelf_INV1"
55         self.labels = "Bottles"
56         self.status = status
57         self.currentCount = 1
58         self.maxCount = 5
59         self.time = time
60
61     def setstatus(self, status):
62         self.status = status
63
64     def getstatus(self):
65         return self.status
66
67     def setcurrentCount(self, count):
68         self.currentCount = count
69
70     def getcurrentCount(self):
71         return self.currentCount
72
73     def setmaxCount(self, count):
74         self.maxCount = count
75
76     def getmaxCount(self):
77         return self.maxCount
78
79     def setlabel(self, labels):
80         self.labels = labels
81
82     def getlabel(self):
83         return self.labels
84
85     def settime(self, time):
86         self.labels = time
87
88     def gettime(self):
89         return self.time
90
91
92 # temporary copied capture_frame file to this due to docker issue for module
93 # import
94 class VideoCamera(object):
95     """
96     opneCV to capture frame from a camera
97     """
98     def __init__(self, url):
99         self.video = cv2.VideoCapture(url)
100
101     def delete(self):
102         self.video.release()
103
104     def get_frame(self):
105         """
106         get a frame from camera url
107         """
108         success, image = self.video.read()
109         return success, image
110
111
112 class VideoFile(object):
113     """
114     opneCV to capture frame from a video stream
115     """
116     def __init__(self, video_name):
117         self.video = cv2.VideoCapture(app.config['VIDEO_PATH'] + video_name)
118
119     def delete(self):
120         self.video.release()
121
122     def get_frame(self):
123         """
124         get a frane from stream
125         """
126         success, image = self.video.read()
127         return success, image
128
129
130 def shelf_inventory(video_capture, camera_info, true=None):
131     """
132     shelf_inventory
133     """
134     global count
135     global mock_func
136
137     labels = "Bottles"
138     count_val = 'ObjCount'
139     process_this_frame = 0
140     i = 0
141     url = config.detection_url + "detect"
142     url_get = config.detection_url + "image"
143
144     while True:
145         success, frame = video_capture.get_frame()
146         if not success:
147             print('read frame from file is failed')
148             break
149
150         i = i+1
151         if i < 10:
152             continue
153
154         i = 0
155
156         if process_this_frame == 0:
157             imencoded = cv2.imencode(".jpg", frame)[1]
158             file = {'file': (
159                 'image.jpg', imencoded.tostring(), 'image/jpeg',
160                 {'Expires': '0'})}
161             res = requests.post(url, files=file)
162             data = json.loads(res.text)
163
164             # get image
165             response = requests.get(url_get)
166
167             file = open(app.config['UPLOAD_PATH'] + "sample_image.jpg", "wb")
168             file.write(response.content)
169             file.close()
170
171             inven_info = inventory_info()
172             current_count = data[count_val]
173             if (current_count >= 3):
174                 status = "Mostly Filled"
175             elif (current_count == 2):
176                 status = "Partially Filled"
177             else:
178                 status = "Needs Filling"
179
180             inven_info.setlabel(labels)
181             inven_info.setstatus(status)
182             inven_info.setcurrentCount(current_count)
183             time_sec = time.time()
184             local_time = time.ctime(time_sec)
185             inven_info.time = local_time
186             store_info_db(inven_info)
187             time.sleep(0.30)
188
189
190 def db_drop_table(inven_info):
191     """
192     cleanup measrurment before new trigger
193
194     :param inven_info: inven_info object
195     :return: None
196     """
197     global db_client
198     db_client.drop_measurement(inven_info.type)
199
200
201 def store_info_db(inven_info):
202     """
203     Send "shelf" data to InfluxDB
204
205     :param inven_info: Inventry object
206     :return: None
207     """
208     global db_client
209     json_body = [
210         {
211             "measurement": inven_info.type,
212             "tags": {
213                 "object": "bottles",
214             },
215             "fields": {
216                 "time": inven_info.time,
217                 "status": inven_info.status,
218                 "currentCount": inven_info.currentCount,
219                 "maxCount": inven_info.maxCount,
220             }
221         }]
222     db_client.write_points(json_body)
223
224
225 def retrive_info_db():
226     """
227     Send "shelf" data to InfluxDB
228
229     :param inven_info: Inventry object
230     :return: None
231     """
232     global db_client
233
234     # get data last n data points from DB
235     result = db_client.query('select * from Shelf_INV1 order by desc limit '
236                              '1;')
237
238     # Get points and iterate over each record
239     points = result.get_points(tags={"object": "bottles"})
240
241     # clear the msg list
242     # listOfMsgs.clear()
243     del listOfMsgs[:]
244
245     # iterate points and fill the records and insert to list
246     for point in points:
247         print("status: %s,Time: %s" % (point['status'], point['time']))
248         newdict = {"shelfName": 'Shelf_INV1', "ObjType": "bottles",
249                    "status": point['status'],
250                    "currentCount": point['currentCount'],
251                    "maxCount": point['maxCount'],
252                    "time": point['time']}
253         listOfMsgs.insert(0, newdict)
254
255
256 def create_database():
257     """
258     Connect to InfluxDB and create the database
259
260     :return: None
261     """
262     global db_client
263     proxy = {"http": "http://{}:{}".format(config.IPADDRESS, config.PORT)}
264     db_client = InfluxDBClient(host=config.IPADDRESS, port=config.PORT,
265                                proxies=proxy, database=config.DATABASE_NAME)
266     db_client.create_database(config.DATABASE_NAME)
267
268
269 @app.route('/v1/inventry/table', methods=['GET'])
270 def inventry_table():
271     """
272     return inventry table
273
274     :return: inventry table
275     """
276     retrive_info_db()
277     table = {"InventryData": listOfMsgs}
278     return jsonify(table)
279
280
281 @app.route('/v1/inventry/image', methods=['GET'])
282 def detected_image():
283     """
284     detect images with imposed
285
286     :return: result image
287     """
288     detected_image = app.config['UPLOAD_PATH'] + 'sample_image.jpg'
289     print('file exits:', str(path.exists(detected_image)))
290     status = str(path.exists(detected_image))
291     if status == 'True':
292         # as base64 string
293         with open(detected_image, "rb") as img_file:
294             jpeg_bin = base64.b64encode(img_file.read())
295
296         response = {'image': jpeg_bin}
297         return jsonify(response)
298     else:
299         response = {'image': 'null'}
300         print('file not exist')
301         return jsonify(response)
302
303
304 def allowed_videofile(filename):
305     """
306     File types to upload:mp4
307     param: filename:
308     """
309     return '.' in filename and \
310            filename.rsplit('.', 1)[1].lower() in ALLOWED_VIDEO_EXTENSIONS
311
312
313 @app.route('/v1/monitor/video', methods=['POST'])
314 def upload_video():
315     app.logger.info("Received message from ClientIP [" + request.remote_addr
316                     + "] Operation [" + request.method + "]" +
317                     " Resource [" + request.url + "]")
318     print("videpath:" + app.config['VIDEO_PATH'])
319     if 'file' in request.files:
320         files = request.files.getlist("file")
321         for file in files:
322             if allowed_videofile(file.filename):
323                 file.save(os.path.join(app.config['VIDEO_PATH'],
324                                        file.filename))
325                 print('file path is:', app.config['VIDEO_PATH']
326                       + file.filename)
327             else:
328                 raise IOError('video format error')
329                 msg = {"responce": "failure"}
330                 return jsonify(msg)
331     msg = {"responce": "success"}
332     return jsonify(msg)
333
334
335 def hash_func(camera_info):
336     hash_string = camera_info["cameraNumber"] + \
337                   camera_info["cameraLocation"] + \
338                   camera_info["rtspUrl"]
339     # readable_hash = hashlib.sha256(str(hash_string).encode(
340     # 'utf-8')).hexdigest()
341     readable_hash = hash(hash_string)
342     if readable_hash < 0:
343         readable_hash += sys.maxsize
344     print(readable_hash)
345     return readable_hash
346
347
348 @app.route('/v1/monitor/cameras', methods=['POST'])
349 def add_camera():
350     camera_detail = request.json
351     app.logger.info("Received message from ClientIP [" + request.remote_addr
352                     + "] Operation [" + request.method + "]" +
353                     " Resource [" + request.url + "]")
354     camera_id = hash_func(camera_detail)
355     camera_id = str(camera_id)
356     for camera_info in listOfCameras:
357         if camera_id == camera_info["cameraID"]:
358             msg = {"responce": "failure"}
359             return jsonify(msg)
360             break
361     camera_info = {"cameraID": camera_id,
362                    "cameraNumber": camera_detail["cameraNumber"],
363                    "rtspUrl": camera_detail["rtspUrl"],
364                    "cameraLocation": camera_detail["cameraLocation"]}
365     listOfCameras.append(camera_info)
366     msg = {"responce": "success"}
367     return jsonify(msg)
368
369
370 @app.route('/v1/monitor/cameras/<cameraID>', methods=['GET'])
371 def get_camera(cameraID):
372     """
373     register camera with location
374     """
375     app.logger.info("Received message from ClientIP [" + request.remote_addr
376                     + "] Operation [" + request.method + "]" +
377                     " Resource [" + request.url + "]")
378     valid_id = 0
379     for camera_info in listOfCameras:
380         # cameraID = int(cameraID)
381         if cameraID == camera_info["cameraID"]:
382             valid_id = 1
383             break
384
385     if valid_id == 0:
386         app.logger.info("camera ID is not valid")
387         msg = {"responce": "failure"}
388         return jsonify(msg)
389
390     if "mp4" in camera_info["rtspUrl"]:
391         video_file = VideoFile(camera_info["rtspUrl"])
392         video_dict = {camera_info["cameraNumber"]: video_file}
393         listOfVideos.append(video_dict)
394         # return Response(shelf_inventory(video_file, camera_info[
395         # "cameraNumber"]),
396         #                mimetype='multipart/x-mixed-replace; boundary=frame')
397         shelf_inventory(video_file, camera_info["cameraNumber"])
398         app.logger.info("get_camera: Added json")
399         msg = {"responce": "success"}
400         return jsonify(msg)
401
402     else:
403         video_file = VideoCamera(camera_info["rtspUrl"])
404         video_dict = {camera_info["cameraNumber"]: video_file}
405         listOfVideos.append(video_dict)
406         return Response(shelf_inventory(video_file,
407                         camera_info["cameraNumber"]),
408                         mimetype='multipart/x-mixed-replace; boundary=frame')
409
410
411 @app.route('/v1/monitor/cameras/<camera_name>', methods=['DELETE'])
412 def delete_camera(camera_name):
413     app.logger.info("Received message from ClientIP [" + request.remote_addr
414                     + "] Operation [" + request.method + "]" +
415                     " Resource [" + request.url + "]")
416     for video1 in listOfVideos:
417         if camera_name in video1:
418             video_obj = video1[camera_name]
419             video_obj.delete()
420     for camera in listOfCameras:
421         if camera_name == camera["cameraNumber"]:
422             listOfCameras.remove(camera)
423     return Response("success")
424
425
426 @app.route('/v1/monitor/cameras')
427 def query_cameras():
428     app.logger.info("Received message from ClientIP [" + request.remote_addr
429                     + "] Operation [" + request.method + "]" +
430                     " Resource [" + request.url + "]")
431     camera_info = {"roboCamera": listOfCameras}
432     return jsonify(camera_info)
433
434
435 @app.route('/', methods=['GET'])
436 def hello_world():
437     app.logger.info("Received message from ClientIP [" + request.remote_addr
438                     + "] Operation [" + request.method + "]" +
439                     " Resource [" + request.url + "]")
440     return Response("Hello MEC Developer")
441
442
443 def start_server(handler):
444     app.logger.addHandler(handler)
445     create_database()
446     if config.ssl_enabled:
447         context = (config.ssl_certfilepath, config.ssl_keyfilepath)
448         app.run(host=config.server_address, debug=True, ssl_context=context,
449                 threaded=True, port=config.server_port)
450     else:
451         app.run(host=config.server_address, debug=True, threaded=True,
452                 port=config.server_port)