From ab6ca080771b706a310ebfd8a4192841cdfef05c Mon Sep 17 00:00:00 2001 From: Private Island Networks Inc Date: Tue, 3 Mar 2026 15:56:53 -0500 Subject: initial commit of experimental code base for PI Explorer (PI-EXP) --- .gitignore | 9 + LICENSE | 201 + README.txt | 13 + constants.py | 164 + main.py | 479 ++ static/css/base.css | 440 ++ static/css/ss.css | 55 + static/images/favicon.ico | Bin 0 -> 198 bytes static/images/icons-svg/action-black.svg | 37 + static/images/icons-svg/action-white.svg | 37 + static/images/icons-svg/alert-black.svg | 38 + static/images/icons-svg/alert-white.svg | 38 + static/images/icons-svg/arrow-d-black.svg | 37 + static/images/icons-svg/arrow-d-l-black.svg | 37 + static/images/icons-svg/arrow-d-l-white.svg | 37 + static/images/icons-svg/arrow-d-r-black.svg | 37 + static/images/icons-svg/arrow-d-r-white.svg | 37 + static/images/icons-svg/arrow-d-white.svg | 37 + static/images/icons-svg/arrow-l-black.svg | 37 + static/images/icons-svg/arrow-l-white.svg | 37 + static/images/icons-svg/arrow-r-black.svg | 37 + static/images/icons-svg/arrow-r-white.svg | 37 + static/images/icons-svg/arrow-u-black.svg | 37 + static/images/icons-svg/arrow-u-l-black.svg | 37 + static/images/icons-svg/arrow-u-l-white.svg | 37 + static/images/icons-svg/arrow-u-r-black.svg | 37 + static/images/icons-svg/arrow-u-r-white.svg | 37 + static/images/icons-svg/arrow-u-white.svg | 37 + static/images/icons-svg/audio-black.svg | 40 + static/images/icons-svg/audio-white.svg | 40 + static/images/icons-svg/back-black.svg | 37 + static/images/icons-svg/back-white.svg | 37 + static/images/icons-svg/bars-black.svg | 38 + static/images/icons-svg/bars-white.svg | 38 + static/images/icons-svg/bullets-black.svg | 40 + static/images/icons-svg/bullets-white.svg | 40 + static/images/icons-svg/calendar-black.svg | 39 + static/images/icons-svg/calendar-white.svg | 39 + static/images/icons-svg/camera-black.svg | 39 + static/images/icons-svg/camera-white.svg | 39 + static/images/icons-svg/carat-d-black.svg | 37 + static/images/icons-svg/carat-d-white.svg | 37 + static/images/icons-svg/carat-l-black.svg | 37 + static/images/icons-svg/carat-l-white.svg | 37 + static/images/icons-svg/carat-r-black.svg | 37 + static/images/icons-svg/carat-r-white.svg | 37 + static/images/icons-svg/carat-u-black.svg | 37 + static/images/icons-svg/carat-u-white.svg | 37 + static/images/icons-svg/check-black.svg | 37 + static/images/icons-svg/check-white.svg | 37 + static/images/icons-svg/clock-black.svg | 38 + static/images/icons-svg/clock-white.svg | 38 + static/images/icons-svg/cloud-black.svg | 39 + static/images/icons-svg/cloud-white.svg | 39 + static/images/icons-svg/comment-black.svg | 37 + static/images/icons-svg/comment-white.svg | 37 + static/images/icons-svg/delete-black.svg | 37 + static/images/icons-svg/delete-white.svg | 37 + static/images/icons-svg/edit-black.svg | 37 + static/images/icons-svg/edit-white.svg | 37 + static/images/icons-svg/eye-black.svg | 38 + static/images/icons-svg/eye-white.svg | 38 + static/images/icons-svg/forbidden-black.svg | 40 + static/images/icons-svg/forbidden-white.svg | 40 + static/images/icons-svg/forward-black.svg | 37 + static/images/icons-svg/forward-white.svg | 37 + static/images/icons-svg/gear-black.svg | 52 + static/images/icons-svg/gear-white.svg | 52 + static/images/icons-svg/grid-black.svg | 44 + static/images/icons-svg/grid-white.svg | 44 + static/images/icons-svg/heart-black.svg | 37 + static/images/icons-svg/heart-white.svg | 37 + static/images/icons-svg/home-black.svg | 37 + static/images/icons-svg/home-white.svg | 37 + static/images/icons-svg/info-black.svg | 38 + static/images/icons-svg/info-white.svg | 38 + static/images/icons-svg/location-black.svg | 38 + static/images/icons-svg/location-white.svg | 38 + static/images/icons-svg/lock-black.svg | 38 + static/images/icons-svg/lock-white.svg | 38 + static/images/icons-svg/mail-black.svg | 37 + static/images/icons-svg/mail-white.svg | 37 + static/images/icons-svg/minus-black.svg | 37 + static/images/icons-svg/minus-white.svg | 37 + static/images/icons-svg/navigation-black.svg | 37 + static/images/icons-svg/navigation-white.svg | 37 + static/images/icons-svg/phone-black.svg | 43 + static/images/icons-svg/phone-white.svg | 43 + static/images/icons-svg/plus-black.svg | 37 + static/images/icons-svg/plus-white.svg | 37 + static/images/icons-svg/power-black.svg | 41 + static/images/icons-svg/power-white.svg | 41 + static/images/icons-svg/recycle-black.svg | 38 + static/images/icons-svg/recycle-white.svg | 38 + static/images/icons-svg/refresh-black.svg | 40 + static/images/icons-svg/refresh-white.svg | 40 + static/images/icons-svg/search-black.svg | 40 + static/images/icons-svg/search-white.svg | 40 + static/images/icons-svg/shop-black.svg | 39 + static/images/icons-svg/shop-white.svg | 39 + static/images/icons-svg/star-black.svg | 37 + static/images/icons-svg/star-white.svg | 37 + static/images/icons-svg/tag-black.svg | 37 + static/images/icons-svg/tag-white.svg | 37 + static/images/icons-svg/user-black.svg | 41 + static/images/icons-svg/user-white.svg | 41 + static/images/icons-svg/video-black.svg | 37 + static/images/icons-svg/video-white.svg | 37 + static/images/loading.gif | Bin 0 -> 3208 bytes static/js/base.js | 200 + static/js/controller.js | 93 + static/js/lmmi.js | 39 + static/js/main.js | 118 + static/js/mdio.js | 36 + static/js/mle.js | 35 + static/js/modal.js | 120 + static/js/ss.js | 302 ++ static/js/ws.js | 147 + templates/base.html | 53 + templates/controller.html | 50 + templates/divider.html | 3 + templates/fabric.html | 24 + templates/help.html | 29 + templates/index.html | 18 + templates/lmmi.html | 43 + templates/mdio.html | 44 + templates/mle.html | 44 + templates/pi_arch_ml.svg | 5996 ++++++++++++++++++++++++++ 128 files changed, 12589 insertions(+) create mode 100755 .gitignore create mode 100644 LICENSE create mode 100644 README.txt create mode 100755 constants.py create mode 100755 main.py create mode 100644 static/css/base.css create mode 100644 static/css/ss.css create mode 100644 static/images/favicon.ico create mode 100644 static/images/icons-svg/action-black.svg create mode 100644 static/images/icons-svg/action-white.svg create mode 100644 static/images/icons-svg/alert-black.svg create mode 100644 static/images/icons-svg/alert-white.svg create mode 100644 static/images/icons-svg/arrow-d-black.svg create mode 100644 static/images/icons-svg/arrow-d-l-black.svg create mode 100644 static/images/icons-svg/arrow-d-l-white.svg create mode 100644 static/images/icons-svg/arrow-d-r-black.svg create mode 100644 static/images/icons-svg/arrow-d-r-white.svg create mode 100644 static/images/icons-svg/arrow-d-white.svg create mode 100644 static/images/icons-svg/arrow-l-black.svg create mode 100644 static/images/icons-svg/arrow-l-white.svg create mode 100644 static/images/icons-svg/arrow-r-black.svg create mode 100644 static/images/icons-svg/arrow-r-white.svg create mode 100644 static/images/icons-svg/arrow-u-black.svg create mode 100644 static/images/icons-svg/arrow-u-l-black.svg create mode 100644 static/images/icons-svg/arrow-u-l-white.svg create mode 100644 static/images/icons-svg/arrow-u-r-black.svg create mode 100644 static/images/icons-svg/arrow-u-r-white.svg create mode 100644 static/images/icons-svg/arrow-u-white.svg create mode 100644 static/images/icons-svg/audio-black.svg create mode 100644 static/images/icons-svg/audio-white.svg create mode 100644 static/images/icons-svg/back-black.svg create mode 100644 static/images/icons-svg/back-white.svg create mode 100644 static/images/icons-svg/bars-black.svg create mode 100644 static/images/icons-svg/bars-white.svg create mode 100644 static/images/icons-svg/bullets-black.svg create mode 100644 static/images/icons-svg/bullets-white.svg create mode 100644 static/images/icons-svg/calendar-black.svg create mode 100644 static/images/icons-svg/calendar-white.svg create mode 100644 static/images/icons-svg/camera-black.svg create mode 100644 static/images/icons-svg/camera-white.svg create mode 100644 static/images/icons-svg/carat-d-black.svg create mode 100644 static/images/icons-svg/carat-d-white.svg create mode 100644 static/images/icons-svg/carat-l-black.svg create mode 100644 static/images/icons-svg/carat-l-white.svg create mode 100644 static/images/icons-svg/carat-r-black.svg create mode 100644 static/images/icons-svg/carat-r-white.svg create mode 100644 static/images/icons-svg/carat-u-black.svg create mode 100644 static/images/icons-svg/carat-u-white.svg create mode 100644 static/images/icons-svg/check-black.svg create mode 100644 static/images/icons-svg/check-white.svg create mode 100644 static/images/icons-svg/clock-black.svg create mode 100644 static/images/icons-svg/clock-white.svg create mode 100644 static/images/icons-svg/cloud-black.svg create mode 100644 static/images/icons-svg/cloud-white.svg create mode 100644 static/images/icons-svg/comment-black.svg create mode 100644 static/images/icons-svg/comment-white.svg create mode 100644 static/images/icons-svg/delete-black.svg create mode 100644 static/images/icons-svg/delete-white.svg create mode 100644 static/images/icons-svg/edit-black.svg create mode 100644 static/images/icons-svg/edit-white.svg create mode 100644 static/images/icons-svg/eye-black.svg create mode 100644 static/images/icons-svg/eye-white.svg create mode 100644 static/images/icons-svg/forbidden-black.svg create mode 100644 static/images/icons-svg/forbidden-white.svg create mode 100644 static/images/icons-svg/forward-black.svg create mode 100644 static/images/icons-svg/forward-white.svg create mode 100644 static/images/icons-svg/gear-black.svg create mode 100644 static/images/icons-svg/gear-white.svg create mode 100644 static/images/icons-svg/grid-black.svg create mode 100644 static/images/icons-svg/grid-white.svg create mode 100644 static/images/icons-svg/heart-black.svg create mode 100644 static/images/icons-svg/heart-white.svg create mode 100644 static/images/icons-svg/home-black.svg create mode 100644 static/images/icons-svg/home-white.svg create mode 100644 static/images/icons-svg/info-black.svg create mode 100644 static/images/icons-svg/info-white.svg create mode 100644 static/images/icons-svg/location-black.svg create mode 100644 static/images/icons-svg/location-white.svg create mode 100644 static/images/icons-svg/lock-black.svg create mode 100644 static/images/icons-svg/lock-white.svg create mode 100644 static/images/icons-svg/mail-black.svg create mode 100644 static/images/icons-svg/mail-white.svg create mode 100644 static/images/icons-svg/minus-black.svg create mode 100644 static/images/icons-svg/minus-white.svg create mode 100644 static/images/icons-svg/navigation-black.svg create mode 100644 static/images/icons-svg/navigation-white.svg create mode 100644 static/images/icons-svg/phone-black.svg create mode 100644 static/images/icons-svg/phone-white.svg create mode 100644 static/images/icons-svg/plus-black.svg create mode 100644 static/images/icons-svg/plus-white.svg create mode 100644 static/images/icons-svg/power-black.svg create mode 100644 static/images/icons-svg/power-white.svg create mode 100644 static/images/icons-svg/recycle-black.svg create mode 100644 static/images/icons-svg/recycle-white.svg create mode 100644 static/images/icons-svg/refresh-black.svg create mode 100644 static/images/icons-svg/refresh-white.svg create mode 100644 static/images/icons-svg/search-black.svg create mode 100644 static/images/icons-svg/search-white.svg create mode 100644 static/images/icons-svg/shop-black.svg create mode 100644 static/images/icons-svg/shop-white.svg create mode 100644 static/images/icons-svg/star-black.svg create mode 100644 static/images/icons-svg/star-white.svg create mode 100644 static/images/icons-svg/tag-black.svg create mode 100644 static/images/icons-svg/tag-white.svg create mode 100644 static/images/icons-svg/user-black.svg create mode 100644 static/images/icons-svg/user-white.svg create mode 100644 static/images/icons-svg/video-black.svg create mode 100644 static/images/icons-svg/video-white.svg create mode 100644 static/images/loading.gif create mode 100644 static/js/base.js create mode 100644 static/js/controller.js create mode 100644 static/js/lmmi.js create mode 100644 static/js/main.js create mode 100644 static/js/mdio.js create mode 100644 static/js/mle.js create mode 100644 static/js/modal.js create mode 100644 static/js/ss.js create mode 100644 static/js/ws.js create mode 100644 templates/base.html create mode 100644 templates/controller.html create mode 100644 templates/divider.html create mode 100644 templates/fabric.html create mode 100644 templates/help.html create mode 100755 templates/index.html create mode 100644 templates/lmmi.html create mode 100644 templates/mdio.html create mode 100644 templates/mle.html create mode 100644 templates/pi_arch_ml.svg diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..aa60a65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__ +.project +.pydevproject +*.orig +*.pyc +*.bak +logs +.DS* + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..c5d6d75 --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +Copyright (C) 2026 Private Island Networks Inc. + +See the included LICENSE file and file headers for licensing information. + +The PI Explorer Project provides a Web-based interaction with the Private Island FPGA-based Project via a LAN. + +This project is currently in experimental, rough form. + +It utilizes aiohttp as the Web framework and Pyshark/Tshark for capturing traffic from the LAN. + +Many improvements are planned + + diff --git a/constants.py b/constants.py new file mode 100755 index 0000000..e83a2fb --- /dev/null +++ b/constants.py @@ -0,0 +1,164 @@ +""" + Copyright (C) 2026 Private Island Networks Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + file: constants.py + +""" + +class STATUS: + OK = 10 + FORM_OK = OK + DONE = OK + NONE = OK + FORM_ERRORS = 20 + FORM_DUPLICATE = 30 + + # pass the URL in data + REDIRECT = 100 + + UNBOUND_FORM = 0 + CSRF_DETECTED = -1 + ERROR_PROCESSING_PARAMS = -2 + ERROR_SERVER = -3 + ERROR_GENERAL = -3 + ERROR_NO_COOKIES = -4 + ERROR_TAMPER = -5 + + # DB Related + ERROR_DB_TOPIC_DOESNT_EXIST = -10 + ERROR_DB_COMMENT_DOESNT_EXIST = -11 + ERROR_DB_OBJECT_DOESNT_EXIST = -12 + + # Session Related + ERROR_SN_DATA_MISMATCH = -20 + + # Catch All View Problems + ERROR_VW_BAD_ARGS = -40 + + # Called an AJAX helper but the request wasn't AJAX + ERROR_AJAX_GETTER_CALLED_WITHOUT_AJAX = -60 + +# Betsy Uses Static IP addressing +BETSY_IP_ADDRESS = "192.168.5.100" +PC_IP_ADDRESS = "192.168.5.44" + + +MODE_IDLE = 0 +MODE_LIVE = 1 +MODE_FILE = 2 + +PORT_PHY0 = 0 +PORT_PHY1 = 1 +PORT_PHY2 = 2 +PORT_PC = 10 +PORT_UNDEFINED = -1 + + +MSG_FORMAT_BASIC = 0 +MSG_FORMAT_CONTROLLER = 1 +MSG_FORMAT_MLE = 2 + +# Controller Message Protocol: +# Type: 8-bit +# Token: 8-bit +# Address: 16-bit +# Data: 32-bit + +# Betsy Address Map +MSG_CONTROLLER_ADDR = 0x0000 +MSG_MAC_ADDR = 0x0100 +MSG_PKT_FILTER_ADDR = 0x0200 +MSG_SWITCH_ADDR = 0x0500 +MSG_MDIO_ADDR = 0x0600 +MSG_INVALID_ADDR = 0x0700 + +# Message Types +MSG_TYPE_NULL = 0 +MSG_TYPE_WRITE = 1 +MSG_TYPE_READ = 2 +MSG_TYPE_NOTIFY_SUCCESS = 3 +MSG_TYPE_NOTIFY_ERROR = 4 +MSG_TYPE_NOTIFY_NOTIFY = 5 +MSG_TYPE_NOTIFY_MLE = 16 + +MSG_TYPE_CONTROL = -1 + +MSG_TYPE_MAP = {MSG_TYPE_NULL:"NULL", + MSG_TYPE_WRITE:"WRITE", + MSG_TYPE_READ: "READ", + MSG_TYPE_NOTIFY_SUCCESS:"SUCCESS", + MSG_TYPE_NOTIFY_ERROR:"ERROR", + MSG_TYPE_NOTIFY_MLE: "MLE"} + +BETSY_ADDR_MAP = { + "0000": "FW_VER", + "0002": "FW_INC", + "0010": "FPGA_TEMP", + "0100": "PHY0_STATUS", + "0104": "PHY1_STATUS", + "0106": "PHY1_MASK", + "0108": "PHY2_STATUS", + "010A": "PHY2_MASK" + } + +DP83867_MAP = { + 0x0000: ["Basic Mode Control Register (BMCR)", "0x1140"], + 0x0001: ["Basic Mode Status Register(BMSR)", "0x796D"], + 0x0002: ["PHY Identifier Register #1(PHYIDR1)", "0x0000"], + 0x0003: ["PHY Identifier Register #2(PHYIDR2)", "0x0000"], + 0x0004: ["Auto-Negotiation Advertisement Register(ANAR)", "0x0000"], + 0x0005: ["Auto-Negotiation Link Partner Ability Register(ANLPAR)", "0x0000"], + 0x0006: ["Auto-Negotiate Expansion Register(ANER)", "0x0000"], + 0x0007: ["Auto-Negotiation Next Page Transmit Register(ANNPTR)", "0x0000"], + 0x0008: ["Auto-Negotiation Next Page Receive Register(ANNPRR)", "0x0000"], + 0x0009: ["Configuration Register 1(CFG1)", "0x0000"], + 0x000A: ["Status Register 1(STS1)", "0x0000"], + 0x000B: ["Undefined", "0x0000"], + 0x000C: ["Undefined", "0x0000"], + 0x000D: ["Register Control Register(REGCR)", "0x0000"], + 0x000E: ["Address or Data Register(ADDAR)", "0x0000"], + 0x000F: ["1000BASE-T Status Register(1KSCR)", "0x0000"], + } + +# Lattice Certus NX SGMIICDR +SGMII_CDR_MAP = { + 0x0000: ["LMMI[0]", "0x60"], + 0x0001: ["LMMI[1]", "0xA1"], + 0x0002: ["LMMI[2]", "0xD2"], + 0x0003: ["LMMI[3]", "0x17"], + 0x0004: ["LMMI[4]", "0xFF"], + 0x0005: ["LMMI[5]", "0x00"], + 0x0006: ["LMMI[6]", "0x55"], + 0x0007: ["LMMI[7]", "0x80"], + 0x0008: ["LMMI[8]", "0x20"], + 0x0009: ["LMMI[9]", "0x40"], + 0x000A: ["LMMI[A]", "0xAF"], + 0x000B: ["LMMI[B]", "0x7F"], + 0x000C: ["LMMI[C]", "0x8F"], + 0x000D: ["LMMI[D]", "0x00"], + 0x000E: ["LMMI[E]", "0x00"], + 0x000F: ["LMMI[F]", "0x00"], + } + +CONTROLLER_MAP = {} + +ML_ENGINE_MAP = {} + +FABRIC_MAP = {} + + + + + diff --git a/main.py b/main.py new file mode 100755 index 0000000..4640df1 --- /dev/null +++ b/main.py @@ -0,0 +1,479 @@ +""" + Copyright (C) 2026 Private Island Networks Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + file: main.py + +""" +import os +import sys +import argparse +import logging +import aiohttp +import asyncio +import asyncio_dgram +import pyshark +import aiohttp_jinja2 +import jinja2 +from multiprocessing import Process, Manager +from aiohttp import web +from queue import Empty +import platform +from socket import socket, AF_INET, SOCK_DGRAM +from time import sleep +import json +from websockets.asyncio.server import serve + +from constants import * + +DEBUG = True +VERSION = 0.1 +PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) +STATIC_PATH = PROJECT_ROOT + '/static' +TEMPLATE_PATH = PROJECT_ROOT + '/templates' +LOG_PATH = PROJECT_ROOT + '/logs' + +ws_key = web.AppKey("ws_key", dict[str, web.WebSocketResponse]) + +async def _parse_pkt(app, pkt): + """ Parse a Wireshark received packet""" + + # Drop unwanted packets + if not (pkt.ip.src == app['ipaddr_betsy'] or pkt.ip.src == app['ipaddr_pc']): + return None + + try: + data = pkt.data.data.__str__() + except: + if DEBUG: + print('non msg received') + return None + + message = dict() + + try: + msg_type = MSG_TYPE_MAP[int(data[0:2],16)] + msg_token = '0x' + data[2:4] + try: + msg_addr = BETSY_ADDR_MAP[data[4:8]] + except: + msg_addr = '0x' + data[4:8] + + msg_data = '0x' + data[8::] + except Exception as e: + print("mapping error:", str(e)) + pass + + dst_port = pkt.udp.dstport.hex_value + if pkt.ip.src == app['ipaddr_betsy']: + port = PORT_PHY0 + elif pkt.ip.src == app['ipaddr_pc'] and dst_port == 0x9020: + port = PORT_PC + else: + port = PORT_UNDEFINED + + message["format"] = MSG_FORMAT_CONTROLLER + message["time"] = pkt.frame_info.get_field("frame.time_relative").__str__()[0:6] + message['port'] = port + message['type'] = msg_type + message['token'] = msg_token + message['address'] = msg_addr + message['data'] = msg_data + + return message + + +async def home(request): + # i_query = request.app['i_query'] + data = request.match_info.get('data', "") + ctx = _default_context('main') + ctx.update(locals()) + response = aiohttp_jinja2.render_template('index.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + return response + +async def mdio(request): + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + q_cont = request.app['q_cont'] + values = dict() + _query_mdio(request.app) + for i in range(16): + pkt = q_cont.get(block=True) # Query + pkt = q_cont.get(block=True) # Response + msg = pkt.data.data.__str__() + values[i] = int("0x" + msg[10:],16) + + d = json.dumps({'values': values}) + response = web.Response(text=d, status=200) + else: + ctx = _default_context('mdio') + ctx['ss'] = True + ctx['regs'] = DP83867_MAP + response = aiohttp_jinja2.render_template('mdio.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + return response + +async def _send_msg(app, msg_type, token, address, data): + try: + sko=socket(AF_INET,SOCK_DGRAM) + sko.bind((app['ipaddr_pc'],0x3000)) + msg = msg_type + token + address + data + sko.sendto(msg,("192.168.5.100",0x9020)) + except: + print("Controller Command failed in Exception: {0}", sys.exc_info()) + pass + + sko.close() + +async def _udp_rcv(): + """ controller response """ + stream = await asyncio_dgram.bind(("192.168.5.40", 0x8000)) + data, remote_addr = await stream.recv() + return data + stream.close() + +async def controller(request): + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + try: + send_pkt = rcv_pkt = None + data = await request.post() + result = STATUS.OK + q_cont = request.app['q_cont'] + await _send_msg(request.app, int(data.get('type')).to_bytes(1), + int(data.get('token')).to_bytes(1), + int(data.get('address')).to_bytes(2), + int(data.get('data')).to_bytes(4)) + send_pkt = await _parse_pkt(request.app, q_cont.get(block=True)) + if send_pkt['type'] == "READ": + rcv_pkt = await _parse_pkt(request.app, q_cont.get(block=True)) + except Exception as e: + print(str(e)) + result = STATUS.ERROR_SERVER + pass + finally: + resp_d = json.dumps({'r':result, 'd' : [send_pkt, rcv_pkt]}) + response = web.Response(text=resp_d) + else: + ctx = _default_context('controller') + ctx['regs'] = CONTROLLER_MAP + response = aiohttp_jinja2.render_template('controller.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + + return response + + +async def mle(request): + data = request.match_info.get('data', "") + ctx = _default_context('main') + ctx.update({'app_layer': 'mle', 'data': data, 'regs': ML_ENGINE_MAP, 'ws': True }) + ctx.update(locals()) + response = aiohttp_jinja2.render_template('mle.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + return response + + +async def fabric(request): + data = request.match_info.get('data', "") + ctx = _default_context('fabric') + regs = {} + ctx.update(locals()) + response = aiohttp_jinja2.render_template('fabric.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + return response + +async def support(request): + data = request.match_info.get('data', "") + ctx = _default_context('support') + response = aiohttp_jinja2.render_template('help.html', request, ctx) + response.headers['Content-Language'] = 'en-US' + return response + +async def lmmi(request): + q_cont = request.app['q_cont'] + data = request.match_info.get('data', "") + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + values = dict() + _query_lmmi(app) + for i in range(28): + pkt = q_cont.get(block=True) + msg = pkt.data.data.__str__() + if len(msg) == 6 and (msg == '03e0fe' or msg == '03e0ff'): + continue + values[round((i-1)/2)] = int("0x" + msg[12:],16) + + d = json.dumps({'values': values}) + resp = web.Response(text=d, status=200) + return resp + else: + context = {'data': data, 'regs0': SGMII_CDR_MAP, 'regs1': SGMII_CDR_MAP} + response = aiohttp_jinja2.render_template('lmmi.html', request, context) + response.headers['Content-Language'] = 'en-US' + return response + + +async def set_filename(request): + if not request.can_read_body: + raise web.HTTPBadRequest() + + data = await request.json() + ns = request.app['ns'] + ns.filename = data['filename'] + + return web.Response(text="ok", status=200) + + +async def start(request): + if not request.can_read_body: + raise web.HTTPBadRequest() + + data = await request.json() + mode = request.app['mode'] + mode.value = data['mode'] + + return web.Response(text="ok", status=200) + + +async def stop(request): + if not request.can_read_body: + raise web.HTTPBadRequest() + + data = await request.json() + mode = request.app['mode'] + mode.value = MODE_IDLE + + return web.Response(text="ok", status=200) + + +async def get_icon(request): + raise aiohttp.web.HTTPNotFound() + + +# WebSocket handle +async def wslogger(request): + ws = web.WebSocketResponse() + await ws.prepare(request) # onopen is called in the browser + + request.app[ws_key]['primary'] = ws + + pkt = None + num_pkts = 0 + q_mle = request.app['q_mle'] + mode = request.app['mode'] + + async for msg in ws: + if msg.type == web.WSMsgType.TEXT: + while True: + try: + pkt = q_mle.get(block=False) + except Empty: + pass + await asyncio.sleep(1) # let other tasks run + continue + + num_pkts = num_pkts + 1 + if DEBUG: + print('get from Q:', num_pkts) + message = await _parse_pkt(request.app, pkt) + if message: + await ws.send_json(message) + + elif msg.type == web.WSMsgType.CLOSE: + break + + +def _query_lmmi(app): + """ Lattice Memory Mapped Interface """ + try: + sko = socket(AF_INET, SOCK_DGRAM) + sko.bind((app['ipaddr_pc'], 0x3000)) + + # Query the LMMI Registers + for i in range(16): + msg = MSG_TYPE_READ + b'\x06' + i.to_bytes(1) + b'\x00\x00' + sko.sendto(msg, (BETSY_IP_ADDRESS, 0x9020)) + sleep(0.01) + + except: + print("Controller Command failed in Exception: {0}", sys.exc_info()) + pass + finally: + sko.close() + + if DEBUG: + print("query_lmmi finished") + + +def _query_mdio(app): + + try: + sko = socket(AF_INET, SOCK_DGRAM) + sko.bind((app['ipaddr_pc'], 0x3000)) + + # Query the LMMI Registers + for i in range(16): + msg = MSG_TYPE_READ.to_bytes(1) + (i).to_bytes(1) + (MSG_MDIO_ADDR+i).to_bytes(2, byteorder='big') + b'\x00\x00\x00\x00' + sko.sendto(msg, (app['ipaddr_betsy'], 0x9020)) + sleep(0.01) + + except: + print("Controller Command failed in Exception: {0}", sys.exc_info()) + pass + finally: + sko.close() + + if DEBUG: + print("query_lmmi finished") + +async def _shutdown(app): + for ws in app[ws_key].values(): + await ws.close() + app[ws_key].clear() + +def _default_context(app_layer): + return { + "version" : VERSION, + "app_layer" : app_layer + } + +def _run_shark(q_cont, q_mle, ife, df, mode, ns, time): + + num_pkts = 0 + ts = time[0] + tf = time[1] + + cap_live = pyshark.LiveCapture(interface=ife, display_filter=df) + + while True: + + if mode.value == MODE_IDLE: + continue + + # mode is a global set during initialization by the browser + elif mode.value == MODE_LIVE: + cap_live.reset() + for pkt in cap_live.sniff_continuously(): + num_pkts = num_pkts + 1 + try: + msg_type = int(pkt.data.data[0:2],16) + if msg_type < MSG_TYPE_NOTIFY_MLE: + q_cont.put(pkt) + print ('put in Cont Q: ', num_pkts) + elif msg_type == MSG_TYPE_NOTIFY_MLE: + q_mle.put(pkt) + print ('put in MLE Q: ', num_pkts) + except Exception as e: + print(str(e)) + pass + + if mode.value == MODE_IDLE: + break + + + elif mode.value == MODE_FILE: + cap_file = pyshark.FileCapture(input_file=LOG_PATH + ns.filename, display_filter=df) + for pkt in cap_file: + num_pkts = num_pkts + 1 + time = float(pkt.frame_info.get_field("frame.time_relative").__str__()[0:9]) + if time >= ts and time < tf: + q_cont.put(pkt) + if DEBUG: + print ('put in Q: ', num_pkts) + if DEBUG: + print("file replay finished") + + cap_file.close() + mode.value = MODE_IDLE + + +async def make_app(args, manager): + ns = manager.Namespace() + q_cont = manager.Queue() + q_mle = manager.Queue() + + if args.ife: + ife = args.ife + else: + ife = 'WiFi' + + if args.mode == "live": + mode = manager.Value('i', MODE_LIVE) + elif args.mode == "file": + mode = manager.Value('i', MODE_FILE) + else: + mode = manager.Value('i', MODE_IDLE) + + # filename = mp.Value(ctypes.c_char_p, args.file.encode("utf-8")) + ns.filename = args.file + df = args.df + + time = (float(args.ts), float(args.tf)) + + p1 = Process(target=_run_shark, args=(q_cont, q_mle, ife, df, mode, ns, time)) + p1.start() + + app = web.Application() + + app[ws_key] = {} + app['ipaddr_pc'] = args.ipaddr_pc + app['ipaddr_betsy'] = args.ipaddr_betsy + app['i_query'] = args.iq + app['mode'] = mode + app['ns'] = ns + app['q_cont'] = q_cont + app['q_mle'] = q_mle + app['p1'] = p1 + + app.on_shutdown.append(_shutdown) + + app.add_routes([ + web.get('/', home), + web.get('/mdio' , mdio), + web.get('/controller', controller), + web.post('/controller',controller), + web.get('/mle', mle), + web.get('/fabric', fabric), + web.get('/help', support), + web.get('/lmmi', lmmi), + web.post('/start', start), + web.post('/stop', stop), + web.post('/filename', set_filename), + web.get('/favicon.ico', get_icon), + web.get('/logger', wslogger), + web.static('/static', STATIC_PATH)]) + + aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(TEMPLATE_PATH)) + + return app + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Private Island Mind Net', epilog='Version: ' + str(VERSION)) + parser.add_argument('--ipaddr_pc', default="192.168.5.40", help="IP Addr of this PC") + parser.add_argument('--ipaddr_betsy', default="192.168.5.100", help="IP Addr of Betsy") + parser.add_argument('--port', type=int, default=8010, help="Web server port number") + parser.add_argument('-d', '--df', default="ip.addr==192.168.5.100", help='display filter', action='store', required=False) + parser.add_argument('--ife', default='WiFi', help='tshark interface', action='store', required=False) + parser.add_argument('--iq', type=int, default=0, help='enable initial query on startup', action='store', required=False) + parser.add_argument('-m', '--mode', default="live", help='mode live or file replay', action='store', required=False) + parser.add_argument('-f', '--file', default="logs\\test.pcapng", help='Wireshark log file', action='store', required=False) + parser.add_argument('--ts', default="0", help='time start for file replay', action='store', required=False) + parser.add_argument('--tf', default="100000.00", help='time last for file replay', action='store', required=False) + args = parser.parse_args() + filename = args.file + + logging.basicConfig(level=logging.DEBUG) + manager = Manager() + + app = web.run_app(make_app(args, manager), port=args.port) + app['p1'].join() diff --git a/static/css/base.css b/static/css/base.css new file mode 100644 index 0000000..702a034 --- /dev/null +++ b/static/css/base.css @@ -0,0 +1,440 @@ +:root { + --mc-color: #0072b0; + font-size: 1.0em; + box-sizing: border-box; +} + +/* See 3.6 in CID. */ +*, *::before, *::after { + box-sizing: inherit; +} + +/* defaults */ +body { + font-family: Helvetica, Arial, sans-serif; + margin: 1rem; +} + +form { + border: 1px solid black; + border-radius: 1em; + width: 50vw; +} + +a:link { + text-decoration: none; +} + +a:visited { + text-decoration: none; +} + +button { + color: white; + border: 0.1px solid green; + border-radius: 0.2em; + background-color: green; +} + +button.mc-quiet { + box-shadow: inherit; + padding: 5px; +} + +input, select { + border-width: 1px; + border-color: black; + margin: 0.2em 0; +} + +input[type="submit"] { + background-color: green; + color: white; +} + +div#betsy { + width: 98vw; + height: 80vh; +} + +#mc-main-window { + height: 800px; +} + +#mc-header { + color: white; + background-color: var(--mc-color); +} + +#mc-banner { + font-size: 2.0em; + padding: 0.5em; +} + +/* top container for menu */ +#mc-menu { + position: relative; + width: 100%; +} + +/* hamburger */ +#mc-menu-hamburger { + position: absolute; + top: -1.4em; + right: 0.1em; + border: 0; + background-color: transparent; + font-size: 2.5em; + width: 1em; + height: 1em; + line-height: 0.4; + text-indent: 4em; + white-space: nowrap; + overflow: hidden; +} + +/* div parent for mc-menu-list */ +#mc-menu-dropdown { + z-index: 20; + display: none; + position: absolute; + right: 0; + margin: 0; +} + +@media ( max-width : 35em) { + #mc-menu.is-open #mc-menu-dropdown { + display: block; + } +} + +@media ( min-width : 35em) { + #mc-menu-dropdown { + display: block; + position: static; + } +} + +#mc-menu-list { + margin: 0; + padding: 0; + list-style: none; +} + +#mc-menu-list>li { + text-align: center; + border-bottom: 2px solid #185aa5; +} + +#mc-menu-list>li a { + display: inline-block; + color: white; + font-size: 1.1rem; + font-weight: normal; + padding: 0.1em 1em 0.1em 1em; + margin: 0 auto; +} + +@media ( min-width : 35em) { + #mc-menu-list { + display: flex; + position: static; + border: 0; + } + #mc-menu-list>li { + flex: 1; + padding-bottom: 0; + } + #mc-menu-list>li a { + display: inline-block; + border-top: 5px solid var(--mc-color); + border-bottom: 5px solid var(--mc-color); + transition: border-bottom 0.3s ease-out; + } + #mc-menu-list>li a:hover { + border-bottom: 5px solid #00cc00; + } +} + +/* svg */ +.mc-divider { + padding: 0; + margin: 1em; + width: 90%; + height: 0.2em; + stroke: #185aa5; + stroke-width: 1.3px; + stroke-opacity: 0.6; +} + +#mc-log-hdr { + display: flex; + flex-wrap: nowrap; + align-items: flext-start; + height: 5rem; +} + +#mc-log { + max-height: 70%; + background-color: white; + overflow-x: hidden; /* Hide horizontal scrollbar */ + overflow-y: scroll; /* Add vertical scrollbar */ +} + +.mc-log-row { + margin: 1 px; + padding: 1 px; + display: flex; + flex-wrap: nowrap; + align-items: flext-start; +} + +.mc-log-col { + font-size: 1.5rem; +} + +.mc-log-col-time { + padding: 1em 0px; + color: black; + flex-basis: 200px; +} + +.mc-log-col-pc { + padding: 1em 0px; + color: green; + flex-basis: 300px; +} + +.mc-log-col-phy0 { + padding: 1em 0px; + color: blue; + flex-basis: 300px; +} + +.mc-log-col-phy1 { + padding: 1em 0px; + color: red; + flex-basis: 300px; +} + +@media ( min-width : 80em) { + .mc-log-col-pc { + flex-basis: 600px; + } + .mc-log-col-phy0 { + flex-basis: 600px; + } +} + +.mc-table-striped thead th { + border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ +} + +.mc-table-striped tbody th, .mc-table-striped tbody td { + border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ +} + +.mc-table-striped tbody tr:nth-child(odd) td, .mc-table-striped tbody tr:nth-child(odd) th + { + background-color: rgba(0, 139, 0, 0.1); +} + +.mc-table-striped th, .mc-table-striped td { + padding: 1.0em 2.0em; +} + +/* Icons */ +.mc-icons { + margin: 0 auto; + display: block; + text-align: center; + vertical-align: center; +} + +.mc-icon { + width: 28px; + height: 28px; + padding: 0.4em 0.7em; + margin: 1em 0.2em; + background-color: #f0f0f0; + border-radius: 2em; + cursor: pointer; +} + +.mc-icon-small { + width: 14px; + height: 14px; + padding: 0.2em 0.35em; + background-color: #f0f0f0; + border-radius: 1em; + cursor: pointer; +} + +h1 { + /* color: #185aa5; */ + font-size: 2rem; + padding: 0.4em; +} + +p { + color: #185aa5; + margin: 1.2em; + width: 90%; + line-height: 1.4em; + text-align: justify; +} + +button.mc-action { + margin: 5px 5px; + border: 1px solid black; + color: white; + cursor: pointer; + padding: 10px 10px; + border-radius: 4px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 0.8rem; +} + +#mc-mode-box { + border: 1px solid black; + border-radius: 15px; + width: 700px; + padding: 1rem; +} + +/* outer modal container */ +#mc-modal { + display: none; +} + +/* disable the viewport behind the modal box */ +#mc-modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 50; +} + +/* modal box */ +#mc-modal-body { + position: fixed; + border-radius: 1em; + border: 2px #185aa5 solid; + top: 20%; + bottom: 20%; + right: 20%; + left: 20%; + padding: 2em 3em 2em 3em; + background-color: white; + overflow: auto; + z-index: 60; +} + +/* offsets are calculated in js */ +#mc-modal-body.mc-image { + +} + +#mc-modal-body.mc-large { + top: 10%; + bottom: 10%; + right: 10%; + left: 10%; +} + +#mc-modal-body.mc-small { + padding: 1em 1.5em; + margin: 0; + top: 10%; + bottom: 10%; + right: 10%; + left: 10%; + overflow: scroll; +} + +#mc-modal-body.mc-xtra-small { + padding: 1em 1.5em; + margin: 0; + top: 15%; + bottom: 15%; + right: 15%; + left: 15%; + overflow: scroll; +} + +@media ( min-width : 45em) { + #mc-modal-body.mc-small { + top: 15%; + bottom: 15%; + right: 15%; + left: 15%; + } + #mc-modal-body.mc-xtra-small { + top: 35%; + bottom: 35%; + right: 25%; + left: 25%; + } +} + +@media ( min-width : 80em) { + #mc-modal-body.mc-small { + top: 25%; + bottom: 25%; + right: 25%; + left: 25%; + } + #mc-modal-body.mc-xtra-small { + top: 35%; + bottom: 35%; + right: 30%; + left: 30%; + } +} + +/* modal close button */ +button#mc-modal-close { + position: absolute; + background-color: white; + color: black; + top: 0.1em; + right: 0.1em; + cursor: pointer; + font-size: 2em; + height: 1em; + width: 1em; + text-indent: 10em; + overflow: hidden; + border: 0; +} + +/* modal close button */ +#mc-modal-buttons { + position: absolute; + bottom: 1em; + left: 3em; + padding: 0.3em; + border: 0; +} + +#mc-modal-close::after { + position: absolute; + line-height: 0.5; + top: 0.2em; + left: 0.1em; + text-indent: 0; + content: "\00D7"; +} + +/* spinner */ +#mc-loading { + position: fixed; + display: none; + top: 49%; + left: 49%; +} \ No newline at end of file diff --git a/static/css/ss.css b/static/css/ss.css new file mode 100644 index 0000000..09e0efb --- /dev/null +++ b/static/css/ss.css @@ -0,0 +1,55 @@ + +.mc-ss-input { + width: 90%; + font-size: 1.1rem; + min-height: 2.2em; + text-align: left; + margin: 0.5em; +} + +.mc-ss-main-panel table { + margin: 2em 0; +} + +.mc-ss-main-panel table td { + padding: 0.7em 0.3em; + font-size: 0.9rem; +} + +.mc-ss-table th.mc-sortable:hover { + text-decoration: underline; + cursor: pointer; +} + +.mc-table-bit-map, .mc-ss-table { + border-collapse: collapse; + width: 80%; + margin: 0 auto; +} + +.mc-table-basic-no-border th, .mc-table-basic th { + font-weight: bold; +} + +.mc-ss-table { + border: 1px solid black; + padding: 0.3em; + text-decoration: underline; + cursor: pointer; +} + +.mc-ss-table th { + border: 1px solid black; + padding: 0.3em; +} + + +.mc-ss-table td { + border: 1px solid black; + padding: 0.3em; +} + +.mc-table-bit-map td { + border: 1px solid black; + padding: 0.3em; +} \ No newline at end of file diff --git a/static/images/favicon.ico b/static/images/favicon.ico new file mode 100644 index 0000000..bc6bda5 Binary files /dev/null and b/static/images/favicon.ico differ diff --git a/static/images/icons-svg/action-black.svg b/static/images/icons-svg/action-black.svg new file mode 100644 index 0000000..20048d8 --- /dev/null +++ b/static/images/icons-svg/action-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/action-white.svg b/static/images/icons-svg/action-white.svg new file mode 100644 index 0000000..23da40d --- /dev/null +++ b/static/images/icons-svg/action-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/alert-black.svg b/static/images/icons-svg/alert-black.svg new file mode 100644 index 0000000..d4179fe --- /dev/null +++ b/static/images/icons-svg/alert-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/alert-white.svg b/static/images/icons-svg/alert-white.svg new file mode 100644 index 0000000..ff06604 --- /dev/null +++ b/static/images/icons-svg/alert-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-black.svg b/static/images/icons-svg/arrow-d-black.svg new file mode 100644 index 0000000..7a8b7de --- /dev/null +++ b/static/images/icons-svg/arrow-d-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-l-black.svg b/static/images/icons-svg/arrow-d-l-black.svg new file mode 100644 index 0000000..8c8d305 --- /dev/null +++ b/static/images/icons-svg/arrow-d-l-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-l-white.svg b/static/images/icons-svg/arrow-d-l-white.svg new file mode 100644 index 0000000..f7379cd --- /dev/null +++ b/static/images/icons-svg/arrow-d-l-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-r-black.svg b/static/images/icons-svg/arrow-d-r-black.svg new file mode 100644 index 0000000..95861e0 --- /dev/null +++ b/static/images/icons-svg/arrow-d-r-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-r-white.svg b/static/images/icons-svg/arrow-d-r-white.svg new file mode 100644 index 0000000..7874ca8 --- /dev/null +++ b/static/images/icons-svg/arrow-d-r-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-d-white.svg b/static/images/icons-svg/arrow-d-white.svg new file mode 100644 index 0000000..f80ad1b --- /dev/null +++ b/static/images/icons-svg/arrow-d-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-l-black.svg b/static/images/icons-svg/arrow-l-black.svg new file mode 100644 index 0000000..0190649 --- /dev/null +++ b/static/images/icons-svg/arrow-l-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-l-white.svg b/static/images/icons-svg/arrow-l-white.svg new file mode 100644 index 0000000..d9de81d --- /dev/null +++ b/static/images/icons-svg/arrow-l-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-r-black.svg b/static/images/icons-svg/arrow-r-black.svg new file mode 100644 index 0000000..7853c5f --- /dev/null +++ b/static/images/icons-svg/arrow-r-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-r-white.svg b/static/images/icons-svg/arrow-r-white.svg new file mode 100644 index 0000000..8c28fc7 --- /dev/null +++ b/static/images/icons-svg/arrow-r-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-black.svg b/static/images/icons-svg/arrow-u-black.svg new file mode 100644 index 0000000..abf9d90 --- /dev/null +++ b/static/images/icons-svg/arrow-u-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-l-black.svg b/static/images/icons-svg/arrow-u-l-black.svg new file mode 100644 index 0000000..08b5c6d --- /dev/null +++ b/static/images/icons-svg/arrow-u-l-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-l-white.svg b/static/images/icons-svg/arrow-u-l-white.svg new file mode 100644 index 0000000..234471e --- /dev/null +++ b/static/images/icons-svg/arrow-u-l-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-r-black.svg b/static/images/icons-svg/arrow-u-r-black.svg new file mode 100644 index 0000000..db1536d --- /dev/null +++ b/static/images/icons-svg/arrow-u-r-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-r-white.svg b/static/images/icons-svg/arrow-u-r-white.svg new file mode 100644 index 0000000..5a510f2 --- /dev/null +++ b/static/images/icons-svg/arrow-u-r-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/arrow-u-white.svg b/static/images/icons-svg/arrow-u-white.svg new file mode 100644 index 0000000..7e2aeb2 --- /dev/null +++ b/static/images/icons-svg/arrow-u-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/audio-black.svg b/static/images/icons-svg/audio-black.svg new file mode 100644 index 0000000..f3651d7 --- /dev/null +++ b/static/images/icons-svg/audio-black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/audio-white.svg b/static/images/icons-svg/audio-white.svg new file mode 100644 index 0000000..a689699 --- /dev/null +++ b/static/images/icons-svg/audio-white.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/back-black.svg b/static/images/icons-svg/back-black.svg new file mode 100644 index 0000000..d6f928a --- /dev/null +++ b/static/images/icons-svg/back-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/back-white.svg b/static/images/icons-svg/back-white.svg new file mode 100644 index 0000000..b34d43b --- /dev/null +++ b/static/images/icons-svg/back-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/bars-black.svg b/static/images/icons-svg/bars-black.svg new file mode 100644 index 0000000..1aebe36 --- /dev/null +++ b/static/images/icons-svg/bars-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/bars-white.svg b/static/images/icons-svg/bars-white.svg new file mode 100644 index 0000000..98cd320 --- /dev/null +++ b/static/images/icons-svg/bars-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/bullets-black.svg b/static/images/icons-svg/bullets-black.svg new file mode 100644 index 0000000..3bf9680 --- /dev/null +++ b/static/images/icons-svg/bullets-black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/bullets-white.svg b/static/images/icons-svg/bullets-white.svg new file mode 100644 index 0000000..ecc4f95 --- /dev/null +++ b/static/images/icons-svg/bullets-white.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/calendar-black.svg b/static/images/icons-svg/calendar-black.svg new file mode 100644 index 0000000..5c3aede --- /dev/null +++ b/static/images/icons-svg/calendar-black.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/calendar-white.svg b/static/images/icons-svg/calendar-white.svg new file mode 100644 index 0000000..177dab9 --- /dev/null +++ b/static/images/icons-svg/calendar-white.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/camera-black.svg b/static/images/icons-svg/camera-black.svg new file mode 100644 index 0000000..865095d --- /dev/null +++ b/static/images/icons-svg/camera-black.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/camera-white.svg b/static/images/icons-svg/camera-white.svg new file mode 100644 index 0000000..b84265c --- /dev/null +++ b/static/images/icons-svg/camera-white.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-d-black.svg b/static/images/icons-svg/carat-d-black.svg new file mode 100644 index 0000000..b6445a7 --- /dev/null +++ b/static/images/icons-svg/carat-d-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-d-white.svg b/static/images/icons-svg/carat-d-white.svg new file mode 100644 index 0000000..4d6d185 --- /dev/null +++ b/static/images/icons-svg/carat-d-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-l-black.svg b/static/images/icons-svg/carat-l-black.svg new file mode 100644 index 0000000..2c8b91f --- /dev/null +++ b/static/images/icons-svg/carat-l-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-l-white.svg b/static/images/icons-svg/carat-l-white.svg new file mode 100644 index 0000000..e18f613 --- /dev/null +++ b/static/images/icons-svg/carat-l-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-r-black.svg b/static/images/icons-svg/carat-r-black.svg new file mode 100644 index 0000000..57cf497 --- /dev/null +++ b/static/images/icons-svg/carat-r-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-r-white.svg b/static/images/icons-svg/carat-r-white.svg new file mode 100644 index 0000000..b034d95 --- /dev/null +++ b/static/images/icons-svg/carat-r-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-u-black.svg b/static/images/icons-svg/carat-u-black.svg new file mode 100644 index 0000000..533a739 --- /dev/null +++ b/static/images/icons-svg/carat-u-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/carat-u-white.svg b/static/images/icons-svg/carat-u-white.svg new file mode 100644 index 0000000..670e48b --- /dev/null +++ b/static/images/icons-svg/carat-u-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/check-black.svg b/static/images/icons-svg/check-black.svg new file mode 100644 index 0000000..db5b715 --- /dev/null +++ b/static/images/icons-svg/check-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/check-white.svg b/static/images/icons-svg/check-white.svg new file mode 100644 index 0000000..b257fd7 --- /dev/null +++ b/static/images/icons-svg/check-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/clock-black.svg b/static/images/icons-svg/clock-black.svg new file mode 100644 index 0000000..c68abef --- /dev/null +++ b/static/images/icons-svg/clock-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/clock-white.svg b/static/images/icons-svg/clock-white.svg new file mode 100644 index 0000000..5589238 --- /dev/null +++ b/static/images/icons-svg/clock-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/cloud-black.svg b/static/images/icons-svg/cloud-black.svg new file mode 100644 index 0000000..6fa55f4 --- /dev/null +++ b/static/images/icons-svg/cloud-black.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/cloud-white.svg b/static/images/icons-svg/cloud-white.svg new file mode 100644 index 0000000..3e0bde3 --- /dev/null +++ b/static/images/icons-svg/cloud-white.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/comment-black.svg b/static/images/icons-svg/comment-black.svg new file mode 100644 index 0000000..485fcc8 --- /dev/null +++ b/static/images/icons-svg/comment-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/comment-white.svg b/static/images/icons-svg/comment-white.svg new file mode 100644 index 0000000..8d188db --- /dev/null +++ b/static/images/icons-svg/comment-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/delete-black.svg b/static/images/icons-svg/delete-black.svg new file mode 100644 index 0000000..74d9988 --- /dev/null +++ b/static/images/icons-svg/delete-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/delete-white.svg b/static/images/icons-svg/delete-white.svg new file mode 100644 index 0000000..a6cc735 --- /dev/null +++ b/static/images/icons-svg/delete-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/edit-black.svg b/static/images/icons-svg/edit-black.svg new file mode 100644 index 0000000..d908f9d --- /dev/null +++ b/static/images/icons-svg/edit-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/edit-white.svg b/static/images/icons-svg/edit-white.svg new file mode 100644 index 0000000..a88643d --- /dev/null +++ b/static/images/icons-svg/edit-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/eye-black.svg b/static/images/icons-svg/eye-black.svg new file mode 100644 index 0000000..7f96a8e --- /dev/null +++ b/static/images/icons-svg/eye-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/eye-white.svg b/static/images/icons-svg/eye-white.svg new file mode 100644 index 0000000..7b0d0e8 --- /dev/null +++ b/static/images/icons-svg/eye-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/forbidden-black.svg b/static/images/icons-svg/forbidden-black.svg new file mode 100644 index 0000000..fe831bc --- /dev/null +++ b/static/images/icons-svg/forbidden-black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/forbidden-white.svg b/static/images/icons-svg/forbidden-white.svg new file mode 100644 index 0000000..710ac39 --- /dev/null +++ b/static/images/icons-svg/forbidden-white.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/forward-black.svg b/static/images/icons-svg/forward-black.svg new file mode 100644 index 0000000..35af7b6 --- /dev/null +++ b/static/images/icons-svg/forward-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/forward-white.svg b/static/images/icons-svg/forward-white.svg new file mode 100644 index 0000000..5cd2def --- /dev/null +++ b/static/images/icons-svg/forward-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/gear-black.svg b/static/images/icons-svg/gear-black.svg new file mode 100644 index 0000000..4f397c5 --- /dev/null +++ b/static/images/icons-svg/gear-black.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/gear-white.svg b/static/images/icons-svg/gear-white.svg new file mode 100644 index 0000000..3a5668f --- /dev/null +++ b/static/images/icons-svg/gear-white.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/grid-black.svg b/static/images/icons-svg/grid-black.svg new file mode 100644 index 0000000..e692e1f --- /dev/null +++ b/static/images/icons-svg/grid-black.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/grid-white.svg b/static/images/icons-svg/grid-white.svg new file mode 100644 index 0000000..5cfaa7c --- /dev/null +++ b/static/images/icons-svg/grid-white.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/heart-black.svg b/static/images/icons-svg/heart-black.svg new file mode 100644 index 0000000..77373d5 --- /dev/null +++ b/static/images/icons-svg/heart-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/heart-white.svg b/static/images/icons-svg/heart-white.svg new file mode 100644 index 0000000..48fd304 --- /dev/null +++ b/static/images/icons-svg/heart-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/home-black.svg b/static/images/icons-svg/home-black.svg new file mode 100644 index 0000000..a4950a3 --- /dev/null +++ b/static/images/icons-svg/home-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/home-white.svg b/static/images/icons-svg/home-white.svg new file mode 100644 index 0000000..b7c1a61 --- /dev/null +++ b/static/images/icons-svg/home-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/info-black.svg b/static/images/icons-svg/info-black.svg new file mode 100644 index 0000000..151cfa2 --- /dev/null +++ b/static/images/icons-svg/info-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/info-white.svg b/static/images/icons-svg/info-white.svg new file mode 100644 index 0000000..dd0f885 --- /dev/null +++ b/static/images/icons-svg/info-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/location-black.svg b/static/images/icons-svg/location-black.svg new file mode 100644 index 0000000..16a59b8 --- /dev/null +++ b/static/images/icons-svg/location-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/location-white.svg b/static/images/icons-svg/location-white.svg new file mode 100644 index 0000000..ce48397 --- /dev/null +++ b/static/images/icons-svg/location-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/lock-black.svg b/static/images/icons-svg/lock-black.svg new file mode 100644 index 0000000..deb3d48 --- /dev/null +++ b/static/images/icons-svg/lock-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/lock-white.svg b/static/images/icons-svg/lock-white.svg new file mode 100644 index 0000000..543cfbb --- /dev/null +++ b/static/images/icons-svg/lock-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/mail-black.svg b/static/images/icons-svg/mail-black.svg new file mode 100644 index 0000000..827e298 --- /dev/null +++ b/static/images/icons-svg/mail-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/mail-white.svg b/static/images/icons-svg/mail-white.svg new file mode 100644 index 0000000..2f0b717 --- /dev/null +++ b/static/images/icons-svg/mail-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/minus-black.svg b/static/images/icons-svg/minus-black.svg new file mode 100644 index 0000000..cd8aec9 --- /dev/null +++ b/static/images/icons-svg/minus-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/minus-white.svg b/static/images/icons-svg/minus-white.svg new file mode 100644 index 0000000..1e33c63 --- /dev/null +++ b/static/images/icons-svg/minus-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/navigation-black.svg b/static/images/icons-svg/navigation-black.svg new file mode 100644 index 0000000..a09c35e --- /dev/null +++ b/static/images/icons-svg/navigation-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/navigation-white.svg b/static/images/icons-svg/navigation-white.svg new file mode 100644 index 0000000..38e99c3 --- /dev/null +++ b/static/images/icons-svg/navigation-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/phone-black.svg b/static/images/icons-svg/phone-black.svg new file mode 100644 index 0000000..4879050 --- /dev/null +++ b/static/images/icons-svg/phone-black.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/phone-white.svg b/static/images/icons-svg/phone-white.svg new file mode 100644 index 0000000..d180382 --- /dev/null +++ b/static/images/icons-svg/phone-white.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/plus-black.svg b/static/images/icons-svg/plus-black.svg new file mode 100644 index 0000000..2e268b0 --- /dev/null +++ b/static/images/icons-svg/plus-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/plus-white.svg b/static/images/icons-svg/plus-white.svg new file mode 100644 index 0000000..488129f --- /dev/null +++ b/static/images/icons-svg/plus-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/power-black.svg b/static/images/icons-svg/power-black.svg new file mode 100644 index 0000000..c078e27 --- /dev/null +++ b/static/images/icons-svg/power-black.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/power-white.svg b/static/images/icons-svg/power-white.svg new file mode 100644 index 0000000..aa5a7a1 --- /dev/null +++ b/static/images/icons-svg/power-white.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/recycle-black.svg b/static/images/icons-svg/recycle-black.svg new file mode 100644 index 0000000..c481e29 --- /dev/null +++ b/static/images/icons-svg/recycle-black.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/recycle-white.svg b/static/images/icons-svg/recycle-white.svg new file mode 100644 index 0000000..11b9c12 --- /dev/null +++ b/static/images/icons-svg/recycle-white.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/refresh-black.svg b/static/images/icons-svg/refresh-black.svg new file mode 100644 index 0000000..16c978c --- /dev/null +++ b/static/images/icons-svg/refresh-black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/refresh-white.svg b/static/images/icons-svg/refresh-white.svg new file mode 100644 index 0000000..fa370e6 --- /dev/null +++ b/static/images/icons-svg/refresh-white.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/search-black.svg b/static/images/icons-svg/search-black.svg new file mode 100644 index 0000000..2b9e863 --- /dev/null +++ b/static/images/icons-svg/search-black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/search-white.svg b/static/images/icons-svg/search-white.svg new file mode 100644 index 0000000..01ccc7c --- /dev/null +++ b/static/images/icons-svg/search-white.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/shop-black.svg b/static/images/icons-svg/shop-black.svg new file mode 100644 index 0000000..9e1bd39 --- /dev/null +++ b/static/images/icons-svg/shop-black.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/shop-white.svg b/static/images/icons-svg/shop-white.svg new file mode 100644 index 0000000..b98cb4b --- /dev/null +++ b/static/images/icons-svg/shop-white.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/star-black.svg b/static/images/icons-svg/star-black.svg new file mode 100644 index 0000000..f2c1afe --- /dev/null +++ b/static/images/icons-svg/star-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/star-white.svg b/static/images/icons-svg/star-white.svg new file mode 100644 index 0000000..d15188e --- /dev/null +++ b/static/images/icons-svg/star-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/tag-black.svg b/static/images/icons-svg/tag-black.svg new file mode 100644 index 0000000..bd11ae6 --- /dev/null +++ b/static/images/icons-svg/tag-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/tag-white.svg b/static/images/icons-svg/tag-white.svg new file mode 100644 index 0000000..7cd4a50 --- /dev/null +++ b/static/images/icons-svg/tag-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/user-black.svg b/static/images/icons-svg/user-black.svg new file mode 100644 index 0000000..7530c98 --- /dev/null +++ b/static/images/icons-svg/user-black.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/user-white.svg b/static/images/icons-svg/user-white.svg new file mode 100644 index 0000000..5528c2e --- /dev/null +++ b/static/images/icons-svg/user-white.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/video-black.svg b/static/images/icons-svg/video-black.svg new file mode 100644 index 0000000..cc50181 --- /dev/null +++ b/static/images/icons-svg/video-black.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icons-svg/video-white.svg b/static/images/icons-svg/video-white.svg new file mode 100644 index 0000000..ddcdf16 --- /dev/null +++ b/static/images/icons-svg/video-white.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/loading.gif b/static/images/loading.gif new file mode 100644 index 0000000..b143d4f Binary files /dev/null and b/static/images/loading.gif differ diff --git a/static/js/base.js b/static/js/base.js new file mode 100644 index 0000000..2920c25 --- /dev/null +++ b/static/js/base.js @@ -0,0 +1,200 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: base.js + * + */ + + "use strict"; + +const STATUS = { + OK: 10, + FORM: 20, + DUPLICATE: 30, + REDIRECT: 100, + UNBOUND_FORM: 0, + ERROR_CSRF: -1, + ERROR_PARAMS: -2, + ERROR_SERVER: -3, + ERROR_COOKIES: -4, + ERROR_TAMPER: -5, +}; + +const PORT_PHY0 = 0; +const PORT_PHY1 = 1; +const PORT_PHY2 = 2; +const PORT_PC = 10; + +const MSG_FORMAT_BASIC = 0; +const MSG_FORMAT_CONTROLLER = 1; +const MSG_FORMAT_MLE = 2; + +const MODE_IDLE = 0; +const MODE_LIVE = 1; +const MODE_FILE = 2; + +const MSG_TYPE_NULL = 0 +const MSG_TYPE_WRITE = 1; +const MSG_TYPE_READ = 2; +const MSG_TYPE_REPLY_SUCCESS = 3; +const MSG_TYPE_REPLY_ERROR = 4; +const MSG_TYPE_NOTIY = 5; +const MSG_TYPE_MLE = 0X10; +const MSG_MAP = { MSG_TYPE_NULL : "NULL", MSG_TYPE_WRITE : "Write", MSG_TYPE_READ : "Read", + MSG_TYPE_REPLY_SUCCESS : "Success", MSG_TYPE_REPLY_ERROR : "Error", MSG_TYPE_NOTIY : "Notify", + MSG_TYPE_MLE : "MLE"}; + +/** + * Convert the server's response from JSON (string) to JS object form and + * gracefully handle an exception + */ +function getResponse(resp) { + try { + resp = JSON.parse(resp); + } catch (err) { + resp = {}; + resp.d = ""; + resp.r = MC_CONSTANTS.SERVER; + } + + return resp; +} + +function getString(obj) { + var result = ""; + for (var key in obj) { + if (result) { + result += "&"; + } + result += key + "=" + encodeURIComponent(obj[key]); + } + return result; +} + +function getPrefix() { + return window.location.pathname.split('/')[1]; // eg., /todos/ +} + +/** + * Alert the user depending on error returned by the server. Todo: consider + * using jqm popups instead of alert + */ +function processError(resp) { + $.loading('none'); + if (resp.r === MC_CONSTANTS.COOKIES || resp.r === MC_CONSTANTS.CSRF) { + alert("Error saving your submission. Cookies may be blocked. Please modify your browser's settings and try again including refreshing this page"); + } else if (resp.r === MC_CONSTANTS.TAMPER) { + alert("Our web server is in an inconsistent state with your browser. Please refresh your page and try again."); + } else { + alert("Server error. Sorry about the trouble. It's been logged, and our team will look into it"); + } +} + + +function $(selector, el) { + if (!el) { + el = document; + } + return el.querySelector(selector); +} + +$.loading = function(display) { + $('#mc-loading').style.display = display; +}; + + +$.get = function(url, data, callback) { + var xhr = new XMLHttpRequest(); + + xhr.addEventListener("progress", updateProgress); + xhr.addEventListener("load", transferComplete); + xhr.addEventListener("error", transferFailed); + xhr.addEventListener("abort", transferCanceled); + + if (data) { + url += "?" + getString(data); + } + + xhr.open('GET', url, true); + + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + xhr.send(null); + + function updateProgress(event) { + // was:event.loaded / event.total + console.log('updateProgress'); + } + + function transferComplete(event) { + callback(event.target.response); + } + + function transferFailed(event) { + console.log('transferFailed'); + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function transferCanceled(event) { + console.log('transferCanceled'); + } +}; + +$.post = function(url, data, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + if (!(data instanceof FormData)) { + xhr.setRequestHeader("Content-type", + "application/x-www-form-urlencoded"); + data = getString(data); + } + + xhr.onreadystatechange = function(event) { + if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { + callback(event.target.response); + } + }; + + xhr.send(data); +}; + +function $$(selector, el) { + if (!el) { + el = document; + } + return el.querySelectorAll(selector); + // Note: the returned object is a NodeList. + // If you'd like to convert it to a Array for convenience, use this instead: + // return Array.prototype.slice.call(el.querySelectorAll(selector)); +} + +// MDN: Assigning to an undeclared variable throws a ReferenceError +function isStrictMode() { + try { + mc_doesNotExist = true; + } + catch(E) { + console.log(E.message); + return true; + } + return false; +} + + diff --git a/static/js/controller.js b/static/js/controller.js new file mode 100644 index 0000000..a12362b --- /dev/null +++ b/static/js/controller.js @@ -0,0 +1,93 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: controller.js + * + * + */ + +function ajaxData() { + var data = {}; + return data; +} + + +function log_msg(msg) { + let msg_str = ''; + + msg_str += msg.type + ': ' + msg.address + ' ' + msg.data + '
'; + + return msg_str; +} + +function postControllerMsg(event) { + $.loading('block'); + event.preventDefault(); + console.log("postControllerMsg"); + + let fd = new FormData($('#mc-cont_message')); + fd.append('dummy', 'foo') + + $.post("/controller", fd, function(resp) { + $.loading('none'); + resp = JSON.parse(resp); + if (resp.r == STATUS.OK) { + let log = document.getElementById('mc-log'); + let height = 2 * resp.d.length; + for (let i=0; i < resp.d.length; i++) { + msg = resp.d[i]; + if (msg.port == PORT_PC) { + log.innerHTML += '
' + msg.time + '
' + + '
' + log_msg(msg) + '
' + + '
' + + '
'; + } + else if (msg.port == PORT_PHY0) { + log.innerHTML += '
' + msg.time + '
' + + '
' + + '
' + log_msg(msg) + + '
'; + } + } + } + + console.log("done"); + }); + +} + +/* +The DOMContentLoaded event fires as soon as the HTML document has been fully parsed, +which typically occurs long before images, stylesheets, and other external resources +have loaded. +*/ +window.addEventListener("DOMContentLoaded", function(e) { + var MsgForm = $('#mc-cont_message') + try { + // $('button[value=submit]').addEventListener('click', postControllerMsg); + MsgForm.addEventListener('submit', postControllerMsg); + } catch (event) { + } +}); + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + console.log("controller loaded"); +}); \ No newline at end of file diff --git a/static/js/lmmi.js b/static/js/lmmi.js new file mode 100644 index 0000000..5cad4e5 --- /dev/null +++ b/static/js/lmmi.js @@ -0,0 +1,39 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: lmmi.js + * + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + + ss = spreadSheet('.mc-ss-main-panel'); + $('#mc-ss-reload-all').addEventListener('click', ss.getNewTable); + console.log("lmmi loaded"); +}); \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..3caa3a7 --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,118 @@ +/* + * + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: main.js + * + */ + + +"use strict"; + +let selected_mode = MODE_LIVE; + +window.addEventListener("load", function(e) { + // re-initialize controls, as needed + /* + $('#mc-mode-live').checked=true; + $('#mc-filename').value = null; // clear any file that was previously selected + + // set up event listeners + $('#mc-mode-live').addEventListener('change',wl_changeMode); + $('#mc-mode-file').addEventListener('change',wl_changeMode); + $('#mc-filename').addEventListener('change',wl_filename); + $('#mc-start').addEventListener('click', wl_start); + $('#mc-stop').addEventListener('click', wl_stop); + $('#mc-help').addEventListener('click', wl_help); + $('#mc-clear').addEventListener('click', wl_clear); + */ + console.log("strict mode test: ",isStrictMode()); +}); + + +function wl_changeMode(event) { + event.preventDefault(); + console.log('change mode: ',event.target.id); + if (event.target.id == "mc-mode-file") { + selected_mode = MODE_FILE; + } + else { + selected_mode = MODE_LIVE; + } +} + +function wl_filename(event) { + event.preventDefault(); + $.loading('block'); + + let data = { 'filename': event.target.files[0].name }; + + $.post("/filename", data, function(resp) { + console.log("filename"); + }); + + $.loading('none'); +} + +function wl_start(event) { + event.preventDefault(); + let data = { 'mode': selected_mode } + $.loading('block'); + + $.post("/start", data, function(resp) { + $('#mc-mode').innerText = "Running"; + }); + // await new Promise(r => setTimeout(r, 1000)); + $.loading('none'); +} + +function wl_stop(event) { + event.preventDefault(); + let data = { 'mode': MODE_IDLE } + $('#mc-mode').innerText = "Idle"; + $.loading('block'); + $.post("/stop", data, function(resp) { + $('#mc-mode').innerText = "Idle"; + }); + $.loading('none'); +} + +function wl_help(event) { + event.preventDefault(); + m = modal('large'); + /* jshint multistr: true */ + m.display('

GEODSS WebLogger Help:

\ + '); +} + +function wl_clear(event) { + event.preventDefault(); + let log = $('#mc-log'); + while (log.firstChild) { + log.removeChild(log.firstChild); + } +} + +function update_fw_version(fw_version) { + $('#mc-fw-version').innerText = fw_version.substring(2,4); +} + +function update_fw_increment(fw_increment) { + $('#mc-fw-increment').innerText = fw_increment.substring(2,4); +} + +function update_temperature(temperature) { + $('#mc-temperature').innerText = parseInt(temperature,16) + " \xB0F"; +} diff --git a/static/js/mdio.js b/static/js/mdio.js new file mode 100644 index 0000000..9185450 --- /dev/null +++ b/static/js/mdio.js @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: mdio.js + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + var ss = spreadSheet('.mc-ss-main-panel'); + $('#mc-ss-reload-all').addEventListener('click', ss.getNewTable); + console.log("mdio loaded"); +}); \ No newline at end of file diff --git a/static/js/mle.js b/static/js/mle.js new file mode 100644 index 0000000..0a28665 --- /dev/null +++ b/static/js/mle.js @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: mle.js + * + */ + + "use strict"; + +function ajaxData() { + var data = {}; + return data; +} + +/* +The load event is essential for operations that require the entire web page to be fully loaded, +including all dependent resources like images and stylesheets. +This event ensures that every element is available for script manipulation. +*/ +window.addEventListener("load", function(e) { + + console.log("mle loaded"); +}); \ No newline at end of file diff --git a/static/js/modal.js b/static/js/modal.js new file mode 100644 index 0000000..af4763b --- /dev/null +++ b/static/js/modal.js @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: modal.js + * + */ + +"use strict"; + +/* + * Modal TODO: stop using ids + * Only add and remove classes programmatically + * + */ + +function Modal(size) { + var modal = this; + this.modalContainer = $('#mc-modal'); + this.modalBody = $('#mc-modal-body', this.modalContainer); + this.modalBackdrop = $('#mc-modal-backdrop', this.modalContainer); + this.modalClose = $('#mc-modal-close', this.modalContainer); + + this.modalBody.classList=""; + + if (size !== undefined) { + this.modalBody.classList.add('mc-' + size); + } + + this.modalClose.addEventListener('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + modal.modalContainer.style.display = 'none'; + if ($('#mc-modal-content')) { + el = $('#mc-modal-content'); + el.remove(); // IE supports removeNode + } + }); + this.modalBackdrop.addEventListener('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + }); +} + +Modal.prototype = { + constructor : Modal, + display : modal_display, + hide : modal_hide, + clean : modal_clean, +}; + +function modal(size) { + return new Modal(size); +} + +function modal_display(content) { + var img, body, ratio, oldWidth, oldHeight; + this.clean(); + + if (content instanceof HTMLImageElement) { + img = content; + body = $('#mc-modal-body'); + + ratio = img.width / img.height; + + /* todo use the bigger img dimension */ + if (window.innerWidth - img.width < 20) { + img.width = window.innerWidth - 80; + img.height = img.width / ratio; + } + + if (window.innerHeight - img.height < 20) { + img.height = window.innerHeight - 80; + } + + body.style.top = String((window.innerHeight - img.height - 64) / 2) + 'px'; + body.style.bottom = String((window.innerHeight - img.height - 64) / 2) + 'px'; + body.style.left = String((window.innerWidth - img.width - 64) / 2) + 'px'; + body.style.right = String((window.innerWidth - img.width - 64) / 2) + 'px'; + + el = document.createElement("div"); + el.id = "mc-modal-content"; + el.innerHTML = ''; + this.modalBody.appendChild(el); + + } else { + el = document.createElement("div"); + el.id = "mc-modal-content"; + el.innerHTML = content; + this.modalBody.appendChild(el); + var dateInputs = $$('.mc-date', this.modalBody); + dateInputs.forEach(function(val) { + val.addEventListener('click', setDate); + }); + } + + this.modalContainer.style.display = 'block'; +} + +function modal_hide() { + this.modalContainer.style.display = 'none'; +} + +function modal_clean() { + var el; + if ((el = $('#mc-modal-content', this.modalContainer))) { + el.remove(); + } +} \ No newline at end of file diff --git a/static/js/ss.js b/static/js/ss.js new file mode 100644 index 0000000..6f0310f --- /dev/null +++ b/static/js/ss.js @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: ss.js + * + */ + +"use strict"; + +/* constructor spreadsheet */ +function SpreadSheet(selector) { + var ss = this; + this.activeCell = null; + this.panel = $(selector); + this.panel.addEventListener('click', function(event){ + + if (event.target.classList.contains('mc-edit')) { + ss.editCell.call(ss, event); + } + else if (event.target.classList.contains('mc-icon') || event.target.nodeName==="IMG") { + ss.actions.call(ss, event); + } + else if (event.target.classList.contains('mc-sortable')) { + data = ajaxData(); + data.sort=event.target.innerHTML.toLowerCase(); + ss.getNewTable(event,data); + } + }); + this.getNewTable = function(event, data) { + $.loading('block'); + if (data == undefined) { + data = ajaxData(); + } + $.get('/' + getPrefix(), data, function(resp) { + resp = JSON.parse(resp); + let values = resp.values; + console.log(resp.values); + cols=$$(".mc-values"); + for (let i = 0; i < 16; i++) { + cols[i].textContent = "0x" + values[i].toString(16); + } + $.loading('none'); + }); + } +} + +SpreadSheet.prototype = { + constructor: SpreadSheet, + actions: spreadSheet_actions, + editCell: spreadSheet_editCell, + newRow: spreadSheet_newRow +}; + +function spreadSheet(selector) { + return new SpreadSheet(selector); +} + +function spreadSheet_actions(event) { + event.preventDefault(); // this fixed my href bug on 3/2/18! + $.loading('block'); + var ss = this; + var link, icon, td, tr, id, action, txt, el, data, m; + + if (event.target.nodeName==="IMG") { + icon = event.target; + link = icon.parentNode; + } + else { + link=event.target; + } + + action=link.dataset.action; + + td=link.parentNode.parentNode; // link is wrapped in a div and then a td cell + tr = td.parentElement; + id = tr.dataset.id; + + txt = td.innerHTML; + + // modal actions + if (action === 'edit' || action === 'cut' || action === 'notify' || action === 'modify') { + + $.get('/' + getPrefix() + '/' + action + '/' + id, null, + function(resp) { + var resp = getResponse(resp); + $.loading('none'); + + if (action === 'edit') { + m = modal('large'); + } + else { + m = modal(); + } + + if (resp.r === MC_CONSTANTS.REDIRECT) { + window.location.assign(resp.d); + } else if (resp.r === MC_CONSTANTS.NONE) { + m.display(resp.d) + $('button[value="submit"]', m.modalContainer) + .addEventListener('click', submitForm); + } else { + processError(resp); + } + }) + } + + // replace object actions + else if (action === "recalc") { + $.get('/' + getPrefix() + '/' + action + '/' + id, null, function(resp) { + var resp = getResponse(resp); + $.loading('none'); + tr.innerHTML = resp.d; + if (resp.n) { + m = modal('large'); + m.display(resp.n); + } + }); + } + + function submitForm(e) { + console.log('submit Form'); + $.loading('block'); + var form = $('form', m.modalContainer); + // var nodes = form.childNodes; + var data = new FormData(form); + // nodes.forEach(addToFormData, data); + + $.post('/' + getPrefix() + '/' + action + '/' + id, data, function(resp) { + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.hide(); + ss.getNewTable(e); + } else if (resp.r === MC_CONSTANTS.FORM) { + form.innerHTML=resp.d; + $('button[value="submit"]',m.modalContainer).addEventListener('click', submitForm ); + } else { + processError(resp); + } + $.loading('none'); + }); + } +} + +/* set up a handler for a new SpreadSheet item / row */ +function spreadSheet_newRow(selector) { + var ss = this; + var m = modal('large'); + try{ + $(selector).addEventListener('click',function(event) { + $.get('/' + getPrefix() + '/new/', null, function(resp) { + + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.display(resp.d); + $('button[value="submit"]', m.modalContainer).addEventListener('click', submitForm); + } else { + processError(resp); + } + }); + }); + } + catch(event) { + console.log('spreadSheet_newRow problem') + } + + function submitForm(e) { + $.loading('block'); + var form = $('form', m.modalContainer); + var nodes = form.childNodes; + var data = new FormData(); + nodes.forEach(addToFormData, data); + + $.post('/' + getPrefix() + '/new/', data, function(resp) { + resp = getResponse(resp); + if (resp.r === MC_CONSTANTS.NONE) { + m.hide(); + ss.getNewTable(e); + } else if (resp.r === MC_CONSTANTS.FORM) { + form.innerHTML=resp.d; + $('button[value="submit"]',m.modalContainer).addEventListener('click', submitForm ); + } else { + processError(resp); + } + $.loading('none'); + }); + } +} + + +function spreadSheet_editCell(event) { + console.log('editCell'); + var td=event.target; + var tr = td.parentElement; + var id = tr.dataset.id; + var type=td.dataset.type; + var field=td.dataset.field; + var txt = td.innerHTML; + var el, data; + + if (type==="text") { + td.innerHTML=""; + el = document.createElement("input"); + el.addEventListener('keypress', keyHandler); + $('body').addEventListener('click',clickHandler); + // append the input + el.className="mc-ss-input" + el.value=txt; + td.appendChild(el); + el.focus(); + } + + /* TODO: do I need to be worried that the handler won't be set in time? */ + else if (type==="date") { + var cal = calendar('.mc-cal'); + cal.container.addEventListener("evtCalClose", calCloseHandler); + cal.open(td); + } + + /* calendar has been closed */ + function calCloseHandler(event) { + console.log('calCloseHandler'); + cal.container.removeEventListener('evtCalClose',calCloseHandler); + + if (event.detail.cancel) { + return; + } + + $.loading('block'); + data = { + id: id, + field: "date", + value: cal.getSelectedDate().toISOString(), + }; + + $.post('/' + getPrefix() + '/modify', data, function(resp) { + resp = getResponse(resp); + if(resp.r == MC_CONSTANTS.NONE) { + td.innerHTML=resp.d; + } + else { + processError(resp); + } + $.loading('none'); + }); + } + + function keyHandler(event) { + console.log(keyHandler); + var el = event.target; + var key = event.keyCode; + var txt = el.value; + + if (key==27) { + event.preventDefault(); + el.remove(); + td.innerHTML=txt; + } + else if (key==13) { + event.preventDefault(); + var data = { + id: id, + field: field, + value: txt, + }; + + el.remove(); + td.innerHTML=txt; + + $.post('/' + getPrefix() + '/modify', data, function(response) { + console.log("upload text suceeded"); + }); + } + } + + function clickHandler(event) { + console.log('clickHandler'); + if(event.target.nodeName==="TD") { + return; // don't cancel out of a cell we just clicked + } + $('body').removeEventListener('click',clickHandler); + var children = td.childNodes; + children.forEach(function(val) { + val.remove(); + }) + td.innerHTML=txt; + } +} + + + \ No newline at end of file diff --git a/static/js/ws.js b/static/js/ws.js new file mode 100644 index 0000000..bc814ff --- /dev/null +++ b/static/js/ws.js @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2026 Private Island Networks Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * file: ws.js + * + */ + +"use strict"; + +let socket = new WebSocket("ws://localhost:8010/logger"); +console.log("here we go"); +let state = "Wait"; +let log = document.getElementById('mc-log'); + +function log_msg(msg) { + let msg_str = ''; + + if (msg.num_cmds > 1) { + console.log('multi commands'); + } + + for (let i = 0; i < msg.num_cmds; i++) { + if (msg.cmds[i][0].includes('RDREG') || msg.cmds[i][0].includes('GETTEMP')) { + msg_str += msg.cmds[i][0] + ' ' + msg.cmds[i][1] + '
'; + } + else if (msg.cmds[i][0] == "" && (msg.cmds[i][1] == "PHY1_STATUS" || msg.cmds[i][1] == "PHY2_STATUS")) { + let val = parseInt(msg.cmds[i][2], 16) & 0x8; + if (val) { + msg_str += msg.cmds[i][1] + ': LOS = 1' + '
'; + } + else { + msg_str += msg.cmds[i][1] + ': LOS = 0' + '
'; + } + } + else { + msg_str += msg.cmds[i][0] + ' ' + msg.cmds[i][1] + ' ' + msg.cmds[i][2] + '
'; + } + } + + return msg_str; +} + +// setInterval(function(){console.log(state)},1000); + +socket.onopen = function(e) { + console.log("[open] Connection established"); + console.log("Sending to server"); + socket.send("open"); // can send data as a string, Blob, or ArrayBuffer. +}; + +function update_mode(mode) { + if (mode == MODE_IDLE) + $('#mc-mode').innerText = "Idle"; + else if (mode == MODE_LIVE || mode == MODE_FILE) + $('#mc-mode').innerText = "Running"; + else + console.log("unsupported mode") +} + +socket.onmessage = function(event) { + let num_msgs = 0; + + let msg = JSON.parse(event.data); + if (msg.format == MSG_FORMAT_BASIC) { + if (typeof msg['mode'] !== "undefined") { + console.log("mode: ", msg['mode']); + update_mode(msg['mode']); + } + else if (typeof msg['fw_version'] !== "undefined") { + console.log("fw_version: ", msg['fw_version']); + update_fw_version(msg['fw_version']); + } + else if (typeof msg['fw_increment'] !== "undefined") { + console.log("fw_increment: ", msg['fw_increment']); + update_fw_increment(msg['fw_increment']); + } + else if (typeof msg['temperature'] !== "undefined") { + console.log("temperature: ", msg['temperature']); + update_temperature(msg['temperature']); + } + else + console.log("unsupported control"); + + return; + } + + let height = 2 * msg.num_cmds; + if (height == 0) { + height = 2; + } + + num_msgs += 1; + console.log("From server:", num_msgs, ': ', msg); + + if (msg.port == PORT_PC) { + log.innerHTML += '
' + msg.time + '
' + + '
' + log_msg(msg) + '
' + + '
' + + '
'; + } + else if (msg.port == PORT_PHY0) { + log.innerHTML += '
' + msg.time + '
' + + '
' + + '
' + log_msg(msg) + + '
'; + } + else if (msg.port == PORT_PHY1) { + log.innerHTML += '
' + msg.time + '
' + + '
' + + '
' + + '
' + log_msg(msg) + '
'; + } + + // socket.send("request"); + state = "Request" +}; + +socket.onclose = function(event) { + state = "Closed" + if (event.wasClean) { + console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + } else { + // e.g. server process killed or network down + // event.code is usually 1006 in this case + console.log('[close] Connection died'); + } +}; + +socket.onerror = function(error) { + state = "Error" + console.log(`[error]`); +}; + +// When you've finished using the WebSocket connection, +// call the WebSocket method close(): diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..c956dfd --- /dev/null +++ b/templates/base.html @@ -0,0 +1,53 @@ + + + + + + +{{ title }} + + +{% if ss %}{% endif %} + + +
+
+
PI Explorer, Version {{ version }}
+ +
+ +
+ {% block content %} + {% endblock %} +
+
+ + + +
+
+
+ +
+
+ +
+ loading +
+ + + + {% if app_layer %} {% endif %} + {% if ws %} {% endif %} + {% if ss %} {% endif %} + \ No newline at end of file diff --git a/templates/controller.html b/templates/controller.html new file mode 100644 index 0000000..cac36e2 --- /dev/null +++ b/templates/controller.html @@ -0,0 +1,50 @@ + +{% extends "base.html" %} {% block content %} + +

Controller

+
+

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+
+ +
+
Time
+
PC
+
PHY0
+
+ + {% include "divider.html" %} + +
+ +
+ + +{% endblock %} diff --git a/templates/divider.html b/templates/divider.html new file mode 100644 index 0000000..6cdb31a --- /dev/null +++ b/templates/divider.html @@ -0,0 +1,3 @@ + + + diff --git a/templates/fabric.html b/templates/fabric.html new file mode 100644 index 0000000..b97eaec --- /dev/null +++ b/templates/fabric.html @@ -0,0 +1,24 @@ + +{% extends "base.html" %} +{% block content %} + +

Fabric {{ regs }}

+
+ + + + + + + + +
AddressRegisterValue
+
+ + + {% for key, value in regs.items() %} {{ key }}, {{ value }}
+ {% endfor %} + +{% endblock %} \ No newline at end of file diff --git a/templates/help.html b/templates/help.html new file mode 100644 index 0000000..cf21066 --- /dev/null +++ b/templates/help.html @@ -0,0 +1,29 @@ + +{% extends "base.html" %} {% block content %} + +

+ For help, please email Support + or enter question on our forum +

+ +{% include "divider.html" %} + + +

Known Issues, as of 3/3/26:

+ + + + + +{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100755 index 0000000..dc400ce --- /dev/null +++ b/templates/index.html @@ -0,0 +1,18 @@ + +{% extends "base.html" %} +{% block content %} +{% if fpga_query == 1 %} +

+ FPGA Version XX.YY , FPGA Internal Temperature: + ?? +

+ {% endif %} + +
+ {% include "pi_arch_ml.svg" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/lmmi.html b/templates/lmmi.html new file mode 100644 index 0000000..eab3ca4 --- /dev/null +++ b/templates/lmmi.html @@ -0,0 +1,43 @@ + +{% extends "base.html" %} {% block content %} + +

SGMII_CDR 0 Registers

+
+ + + + + + + + + + + + {% for key, value in regs0.items() %} + + + + + + + + {% endfor %} + +
AddressRegisterDefaultValue reload
{{ key }}{{regs0[key][0]}}{{regs0[key][1]}}test +
+ editadmin +
+
+
+
+ +{% endblock %} diff --git a/templates/mdio.html b/templates/mdio.html new file mode 100644 index 0000000..1a7bfb0 --- /dev/null +++ b/templates/mdio.html @@ -0,0 +1,44 @@ + +{% extends "base.html" %} +{% block content %} + +

MDIO PHY0

+
+ + + + + + + + + + + + {% for key, value in regs.items() %} + + + + + + + + {% endfor %} + +
AddressRegisterDefaultValue reload
{{ key }}{{regs[key][0]}}{{regs[key][1]}} +
+ editadmin +
+
+
+
+ +{% endblock %} + + diff --git a/templates/mle.html b/templates/mle.html new file mode 100644 index 0000000..b7bc632 --- /dev/null +++ b/templates/mle.html @@ -0,0 +1,44 @@ + +{% extends "base.html" %} +{% block content %} + +
+

+ Viewing Mode: {{mode}} +

+ + + + + +
+ +
+ +
+
Time
+
PHY0
+
PHY1
+
PHY2
+
+ +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/templates/pi_arch_ml.svg b/templates/pi_arch_ml.svg new file mode 100644 index 0000000..601f184 --- /dev/null +++ b/templates/pi_arch_ml.svg @@ -0,0 +1,5996 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + PHY0 + + + + + PHY2 + + + + switch + + controller + + mdio_cont + + code / params / data + + mdio + + + + + + MDIO + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + + + + RGMII + tx_data + tx_data + + + + + + + MAC + MAC + + + + machine learning engine + + + + PHY0 + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + RGMII + MAC + + + + PHY0 + + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + + MAC + + + + PHY1 + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + MAC + + + + PHY0 + + + rx0_d + tx0_d + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + + + tx_data + + + + MAC + + + + PHY0 + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + MAC + + + + PHY0 + + + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + + MAC + + + + PHY0 + + + + + + + + fcs + + + + + + + + sync fifo + + + + + + + + + + + + + pkt_filter + + + + + + + + + drop_fifo + + Layer 3/4 + + + RGMII + MAC + + + + + + DDRIO + + + rx1_d + tx1_d + + + rx2_d + tx2_d + + + + + + DDRIO + + + + + + DDRIO + + + + + . . . + from rx modules + + Private Island + ® + + Machine Learning Enabled + + + + + -- cgit v1.2.3-8-gadcc