diff --git a/connectcore-demo-example/demoserver.py b/connectcore-demo-example/demoserver.py index 554a0a7..2d62fa2 100755 --- a/connectcore-demo-example/demoserver.py +++ b/connectcore-demo-example/demoserver.py @@ -102,12 +102,15 @@ BT_REQUEST_TIMEOUT = 5000 COMMAND_PING = "ping -q -w 1 -c 1 -I %s 8.8.8.8" COMMAND_READ_SN = "fw_printenv -n serial#" +NPU_DEMOS_FILE = "static/assets/npu_demos.json" + # Variables. log = logging.getLogger(APP_NAME) last_cpu_work = 0 last_cpu_total = 0 led_status = {} fw_process = None +npu_demos = None class RequestHandler(http.server.SimpleHTTPRequestHandler): @@ -624,6 +627,46 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler): self.wfile.write(json.dumps(result).encode(encoding="utf_8")) else: self.wfile.write(json.dumps({"data": json.dumps(result)}).encode(encoding="utf_8")) + elif re.search("/ajax/get_npu_info", self.path) is not None: + # Set the response headers. + self._set_headers(200) + # Fill NPU info. + info = { + "has-npu-demos": str(has_npu_demos()).lower() + } + # Send the JSON value. + self.wfile.write(json.dumps(info).encode(encoding="utf_8")) + elif re.search("/ajax/get_npu_demos", self.path) is not None: + # Set the response headers. + self._set_headers(200) + # Get NPU demos data. + platform_id = get_platform_id() + demos = get_npu_demos_for_platform(platform_id) + self.wfile.write(json.dumps({"data": json.dumps(demos)}).encode(encoding="utf_8")) + elif re.search("/ajax/run_npu_demo", self.path) is not None: + # Set the response headers. + self._set_headers(200) + # Get the JSON data. + data = self.rfile.read(int(self.headers["Content-Length"])) + demo_id = json.loads(data.decode("utf-8")).get("demo_id", None) + # Get the demo with the given ID. + demo = get_npu_demo(demo_id) + if demo: + # Get the command to execute. + script = None + platform_id = get_platform_id() + for comp_platform in demo.get("compatible_platforms", []): + if comp_platform.get("platform", None) == platform_id: + script = comp_platform.get("launch_script", None) + break + if script: + # Execute the demo. + exec_cmd_nowait(script) + self.wfile.write("{}".encode(encoding="utf_8")) + else: + self.wfile.write(json.dumps({"error": "NPU demo launch script not found."}).encode(encoding="utf_8")) + else: + self.wfile.write(json.dumps({"error": "NPU demo not found."}).encode(encoding="utf_8")) else: # Forbidden. self._set_headers(403) @@ -1766,6 +1809,93 @@ def set_bluetooth_device_configuration(device, config_data): return data +def has_npu_demos(): + """ + Returns whether the device has NPU demos available or not. + + Returns: + Boolean: True if device has NPU demos available, False otherwise. + """ + return len(get_npu_demos_for_platform(get_platform_id())) > 0 + + +def load_npu_demos(): + """ + Returns the loaded NPU demos or parses the JSON file with NPU demos + metadata. + + Returns: + list: A list of dictionaries, each representing an NPU demo, + `None` if error + """ + global npu_demos + + # Return the demos if they are already loaded. + if npu_demos: + return npu_demos + + # Initialize the demos var. + npu_demos = None + + # Build demos file path. + npu_demos_file_path = os.path.join(os.path.dirname(__file__), NPU_DEMOS_FILE) + try: + with open(npu_demos_file_path, 'r') as file: + content = json.load(file) + if content: + npu_demos = content.get("npu_demos", []) + except FileNotFoundError: + log.error("Error reading NPU demos: File '%s' not found." % NPU_DEMOS_FILE) + except json.JSONDecodeError: + log.error("Error reading NPU demos: File '%s' contains invalid JSON." % NPU_DEMOS_FILE) + except OSError as exc: + log.error("Error reading NPU demos: %s" % str(exc)) + + return npu_demos + + +def get_npu_demo(demo_id): + """ + Returns the NPU demo matching the given ID. + + Args: + demo_id (str): ID of the NPU demo to get. + + Returns: + Dictionary: The NPU demo for the given ID, None if the demo is not + found. + """ + demos = load_npu_demos() + if not demos: + return None + + return next((demo for demo in demos if demo.get("id", None) == demo_id), None) + + +def get_npu_demos_for_platform(platform_name): + """ + Returns a list of NPU demos that are compatible with the given platform. + + Args: + platform_name (str): The name of the platform to filter NPU demos by. + + Returns: + list: A list of dictionaries, each representing an NPU demo compatible + with the given platform. + """ + compatible_demos = [] + demos = load_npu_demos() + + if demos: + for demo in demos: + for platform in demo.get('compatible_platforms', []): + if platform.get('platform') == platform_name and file_exists(platform.get('launch_script')): + compatible_demos.append(demo) + break + + return compatible_demos + + def get_platform_id(): """ Returns the running platform ID. @@ -1891,6 +2021,19 @@ def write_file(path, value): return True +def file_exists(file_path): + """ + Determines whether the given file path exists or not. + + Args: + file_path (String): Absolute path of the file to check. + + Returns: + Boolean: 'True' if the file exists, 'False' otherwise. + """ + return os.path.isfile(file_path) + + def resize_to(value, to, divider=1024): """ Resizes the given value. diff --git a/connectcore-demo-example/index.html b/connectcore-demo-example/index.html index 3128396..ac502b3 100644 --- a/connectcore-demo-example/index.html +++ b/connectcore-demo-example/index.html @@ -68,6 +68,14 @@ Digi Demo - Dashboard +
  • + +
    + + Machine Learning +
    +
    +
  • @@ -824,6 +832,7 @@ Digi Demo - Dashboard + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + Loading... +
    +
    + +
    +
    + + +
    +
    +
    +
    NPU demos
    + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/connectcore-demo-example/static/assets/npu_demos.json b/connectcore-demo-example/static/assets/npu_demos.json new file mode 100644 index 0000000..144a3d2 --- /dev/null +++ b/connectcore-demo-example/static/assets/npu_demos.json @@ -0,0 +1,92 @@ +{ + "npu_demos": [ + { + "id": "pose_estimation", + "name": "Pose Estimation", + "preview_image": "npu_pose_estimation.png", + "description": "This demo detects and tracks human body joints in real-time using video input, showcasing the NPU's efficiency in handling complex computations for applications like augmented reality and sports analysis.", + "compatible_platforms": [ + { + "platform": "ccmp255-dvk", + "launch_script": "/etc/demos/scripts/npu_pose_estimation.sh" + } + ] + }, + { + "id": "object_detection", + "name": "Object Detection", + "preview_image": "npu_object_detection.png", + "description": "This demo detects and identifies objects in real-time using a camera, demonstrating the NPU's capability to efficiently process visual data for applications like autonomous systems and smart surveillance.", + "compatible_platforms": [ + { + "platform": "ccmp255-dvk", + "launch_script": "/etc/demos/scripts/npu_object_detection.sh" + }, + { + "platform": "ccimx93-dvk", + "launch_script": "/etc/demos/scripts/launch_eiq_demo_object_detection.sh" + } + ] + }, + { + "id": "image_classification", + "name": "Image Classification", + "preview_image": "npu_image_classification.png", + "description": "This demo scans images with a camera and classifies them in real-time, demonstrating the NPU's ability to quickly and accurately categorize visual data for applications like inventory management and automated sorting.", + "compatible_platforms": [ + { + "platform": "ccmp255-dvk", + "launch_script": "/etc/demos/scripts/npu_image_classification.sh" + } + ] + }, + { + "id": "semantic_segmentation", + "name": "Semantic Segmentation", + "preview_image": "npu_semantic_segmentation.png", + "description": "This demo captures images with a camera and performs semantic segmentation in real-time, dividing the scene into distinct regions by type, perfect for applications like robotics and environmental monitoring.", + "compatible_platforms": [ + { + "platform": "ccmp255-dvk", + "launch_script": "/etc/demos/scripts/npu_semantic_segmentation.sh" + } + ] + }, + { + "id": "gesture_detection", + "name": "Gesture Detection", + "preview_image": "npu_gesture_detection.png", + "description": "This demo identifies and interprets hand gestures in real-time, demonstrating the NPU's capacity for responsive interaction in applications such as touchless controls, augmented reality, and smart home automation.", + "compatible_platforms": [ + { + "platform": "ccimx93-dvk", + "launch_script": "/etc/demos/scripts/launch_eiq_demo_gesture_detection.sh" + } + ] + }, + { + "id": "face_recognition", + "name": "Face Recognition", + "preview_image": "npu_face_recognition.png", + "description": "This demo detects and recognizes human faces in real-time using a camera, showcasing the NPU's ability to process visual data efficiently for security systems, personalized user experiences, and access control applications.", + "compatible_platforms": [ + { + "platform": "ccimx93-dvk", + "launch_script": "/etc/demos/scripts/launch_eiq_demo_face_recognition.sh" + } + ] + }, + { + "id": "drowsy_detection", + "name": "Drowsy Detection", + "preview_image": "npu_drowsy_detection.png", + "description": "This demo monitors and detects signs of drowsiness in real-time, using visual cues to assess alertness, highlighting the NPU's potential for enhancing safety in driver assistance systems and fatigue monitoring in workplaces.", + "compatible_platforms": [ + { + "platform": "ccimx93-dvk", + "launch_script": "/etc/demos/scripts/launch_eiq_demo_dms.sh" + } + ] + } + ] +} diff --git a/connectcore-demo-example/static/css/general.css b/connectcore-demo-example/static/css/general.css index 486939b..5681ae2 100644 --- a/connectcore-demo-example/static/css/general.css +++ b/connectcore-demo-example/static/css/general.css @@ -1897,6 +1897,52 @@ body { } /* END MULTIMEDIA VIEWER */ +/* NPU */ +.npu-container { + color: #666666; + padding: 20px 7px 0px 7px; +} + +.npu-box { + padding-left: 0px; + padding-right: 0px; +} + +.npu-item { + cursor: pointer; + padding: 5px; + height: 240px; + border: 1px solid var(--light); + border-radius: 0px; + text-align: center; + background-color: white; + box-shadow: 4px .25rem .25rem rgba(0,0,0,.075); +} + +.npu-item:hover { + border: 1px solid var(--digi-green); +} + +.npu-title { + padding: 0px 0px 3px 0px; + margin-bottom: 0px; + font-weight: bold; + font-size: 17px; +} + +.npu-item hr { + border-width: 1px; + border-color: #CCCCCC; + margin-top: 5px; +} + +.npu-item img { + height: 200px; + object-fit: contain; + max-width: 100%; +} +/* END NPU */ + /* CONFIG CONTROLS */ .param-container { margin-top: 10px; diff --git a/connectcore-demo-example/static/images/npu_drowsy_detection.png b/connectcore-demo-example/static/images/npu_drowsy_detection.png new file mode 100644 index 0000000..4c2d748 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_drowsy_detection.png differ diff --git a/connectcore-demo-example/static/images/npu_face_recognition.png b/connectcore-demo-example/static/images/npu_face_recognition.png new file mode 100644 index 0000000..c010d47 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_face_recognition.png differ diff --git a/connectcore-demo-example/static/images/npu_gesture_detection.png b/connectcore-demo-example/static/images/npu_gesture_detection.png new file mode 100644 index 0000000..52a31fc Binary files /dev/null and b/connectcore-demo-example/static/images/npu_gesture_detection.png differ diff --git a/connectcore-demo-example/static/images/npu_image_classification.png b/connectcore-demo-example/static/images/npu_image_classification.png new file mode 100644 index 0000000..a6c6805 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_image_classification.png differ diff --git a/connectcore-demo-example/static/images/npu_object_detection.png b/connectcore-demo-example/static/images/npu_object_detection.png new file mode 100644 index 0000000..9d60413 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_object_detection.png differ diff --git a/connectcore-demo-example/static/images/npu_pose_estimation.png b/connectcore-demo-example/static/images/npu_pose_estimation.png new file mode 100644 index 0000000..becf2f9 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_pose_estimation.png differ diff --git a/connectcore-demo-example/static/images/npu_semantic_segmentation.png b/connectcore-demo-example/static/images/npu_semantic_segmentation.png new file mode 100644 index 0000000..f32dac9 Binary files /dev/null and b/connectcore-demo-example/static/images/npu_semantic_segmentation.png differ diff --git a/connectcore-demo-example/static/js/common.js b/connectcore-demo-example/static/js/common.js index bcf7ed2..696369b 100644 --- a/connectcore-demo-example/static/js/common.js +++ b/connectcore-demo-example/static/js/common.js @@ -76,6 +76,7 @@ const ID_FLASH_MEMORY = "flash_memory"; const ID_FLASH_SIZE = "flash_size"; const ID_FW_STORE_PATH = "fw_store_path"; const ID_HAS_ARROW = "has-arrow"; +const ID_HAS_NPU_DEMOS = "has-npu-demos"; const ID_HAS_PANEL = "has-panel"; const ID_ICON = "icon"; const ID_ID = "id"; @@ -110,6 +111,7 @@ const ID_SAMPLE_RATE = "sample_rate"; const ID_SECTION_DASHBOARD = "section_dashboard"; const ID_SECTION_MANAGEMENT = "section_management"; const ID_SECTION_MULTIMEDIA = "section_multimedia"; +const ID_SECTION_NPU = "section_npu"; const ID_SERIAL_NUMBER = "serial_number"; const ID_SESSION_ID = "session_id"; const ID_SIZE = "size"; @@ -515,6 +517,11 @@ function isMultimediaShowing() { return window.location.pathname.indexOf("multimedia") > -1; } +// Returns whether the NPU page is showing or not. +function isNPUShowing() { + return window.location.pathname.indexOf("npu") > -1; +} + // Returns the device name. function getDeviceName() { return new URLSearchParams(window.location.search).get(ID_DEVICE_NAME); @@ -522,9 +529,11 @@ function getDeviceName() { // Updates the available web sections. function updateAvailableSections() { - // Remove multimedia section when rendering the demo from a computer. - if (!navigator.platform.includes("aarch") && !navigator.platform.includes("arm")) + // Remove device specific sections when rendering the demo from a computer. + if (!navigator.platform.includes("aarch") && !navigator.platform.includes("arm")) { removeSection(ID_SECTION_MULTIMEDIA); + removeSection(ID_SECTION_NPU); + } // Set visible sections based on device type. $.post( "http://" + getServerAddress() + "/ajax/get_device_type", @@ -535,6 +544,7 @@ function updateAvailableSections() { toastr.error("Could not get device type"); return; } + // Update multimedia section. switch (data[ID_DEVICE_TYPE]) { case CCIMX6ULSBC.DEVICE_TYPE: case CCMP157.DEVICE_TYPE: @@ -547,6 +557,20 @@ function updateAvailableSections() { // Process error. processAjaxErrorResponse(response); }); + // Set NPU section visibility. + $.post( + "http://" + getServerAddress() + "/ajax/get_npu_info", + function(data) { + // Process answer. + if (data[ID_HAS_NPU_DEMOS] != "true") { + // Remove NPU section. + removeSection(ID_SECTION_NPU); + } + } + ).fail(function(response) { + // Process error. + processAjaxErrorResponse(response); + }); } // Removes the given section. diff --git a/connectcore-demo-example/static/js/npu.js b/connectcore-demo-example/static/js/npu.js new file mode 100644 index 0000000..e8070e9 --- /dev/null +++ b/connectcore-demo-example/static/js/npu.js @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2024, Digi International Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Constants. +const ID_NPU_DEMOS = "npu_demos"; +const ID_NPU_IMAGE = "preview_image"; +const ID_NPU_DEMOS_CONTAINER = "npu_demos_container"; + +const CLASS_4_COLUMNS_XL = "col-xl-3" +const CLASS_NPU_DEMO_CONTAINER = "npu-container" + +const TEMPLATE_NPU_DEMO = "" + + "
    " + + "
    " + + "
    {1}
    " + + " {3}" + + "
    " + + "
    "; +const TEMPLATE_NO_DEMOS = "

    No demos found

    "; + +const MESSAGE_EXECUTING_NPU_DEMO = "Executing NPU demo..."; + +const HIDE_STATUS_TIMEOUT = 10000; // 10 seconds. + +// Variables. +var readingNPUInfo = false; +var npuInfoRead = false; + +// Initializes the npu page. +function initializeNPUPage() { + // Sanity checks. + if (!isNPUShowing() || npuInfoRead) + return; + // Read NPU demos information. + readNPUDemos(); +} + +// Read the NPU demos information. +function readNPUDemos() { + // Execute only in the NPU page. + if (!isNPUShowing() || npuInfoRead) + return; + // Flag reading variable. + readingNPUInfo = true; + // Hide the info popup. + showInfoPopup(false); + // Show the loading popup. + showLoadingPopup(true, MESSAGE_LOADING_INFORMATION); + // Send request to read NPU demos information. + $.post( + "http://" + getServerAddress() + "/ajax/get_npu_demos", + function(data) { + readingNPUInfo = false; + // Process only in the NPU page. + if (!isNPUShowing()) + return; + // Hide the loading panel. + showLoadingPopup(false); + // Process the answer. + processNPUDemosResponse(data); + } + ).fail(function(response) { + // Flag reading variable. + readingNPUInfo = false; + // Process only in the NPU page. + if (!isNPUShowing()) + return; + // Hide the loading panel. + showLoadingPopup(false); + // Process error. + processAjaxErrorResponse(response); + }); +} + +// Processes the response of the NPU demos request. +function processNPUDemosResponse(response) { + // Check if there was any error in the request. + if (!checkErrorResponse(response, false)) { + // List the available demos. + listNPUDemos(JSON.parse(response[ID_DATA])); + // Flag device info read. + npuInfoRead = true; + } +} + +// Lists and draws the given NPU demos. +function listNPUDemos(npuDemos) { + var npuDemosContainer = document.getElementById(ID_NPU_DEMOS_CONTAINER); + + // Process response entries and create HTML elements. + if (npuDemos.length === 0) { + var entryDiv = document.createElement("div"); + + entryDiv.innerHTML = TEMPLATE_NO_DEMOS; + npuDemosContainer.appendChild(entryDiv); + } else { + for (var demo of npuDemos) { + var id = demo[ID_ID]; + var name = demo[ID_NAME]; + var image = demo[ID_NPU_IMAGE]; + var entryDiv = document.createElement("div"); + + entryDiv.classList.add(CLASS_4_COLUMNS_XL); + entryDiv.classList.add(CLASS_NPU_DEMO_CONTAINER); + entryDiv.innerHTML = TEMPLATE_NPU_DEMO.format(id, name, image, name); + npuDemosContainer.appendChild(entryDiv); + } + } +} + +// Runs the given NPU demo. +function runNPUDemo(npuDemoID) { + // Hide the info popup. + showInfoPopup(false); + // Show the loading popup. + showLoadingPopup(true, MESSAGE_EXECUTING_NPU_DEMO); + // Send request. + $.post( + "http://" + getServerAddress() + "/ajax/run_npu_demo", + JSON.stringify({ + "demo_id": npuDemoID, + }), + function(data) { + // Process only in the NPU page. + if (!isNPUShowing()) + return; + // Process answer. + processRunNPUDemoResponse(data); + } + ).fail(function(response) { + // Process only in the NPU page. + if (!isNPUShowing()) + return; + // Hide the loading panel. + showLoadingPopup(false); + // Process error. + processAjaxErrorResponse(response); + }); +} + +// Processes the response of the run NPU demo request. +function processRunNPUDemoResponse(response) { + // Check if there was any error in the request. + checkErrorResponse(response, false); + // Start timer to hide loading popup. + setTimeout(() => { + // Process only in the NPU page. + if (!isNPUShowing()) + return; + // Hide the loading panel. + showLoadingPopup(false); + }, HIDE_STATUS_TIMEOUT); +} \ No newline at end of file