feat(uart proxy)
- use vuetify virtual scroll to handle large message known issues: - very slow addItem when frequent add 1 item, become short time normal after a batch item add -> batch add every 20ms
This commit is contained in:
parent
c2b8f6ba09
commit
15c1143b25
|
@ -8,12 +8,15 @@
|
|||
"name": "vue-project",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"element-plus": "^2.6.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"ansi_up": "^6.0.2",
|
||||
"element-plus": "^2.7.3",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.10.2",
|
||||
"vue-router": "^4.3.0"
|
||||
"vue-router": "^4.3.0",
|
||||
"vuetify": "^3.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
|
@ -38,6 +41,7 @@
|
|||
"vite-plugin-css-injected-by-js": "^3.5.0",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-singlefile": "^2.0.1",
|
||||
"vite-plugin-vuetify": "^2.0.3",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^2.0.6"
|
||||
}
|
||||
|
@ -106,7 +110,6 @@
|
|||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
|
@ -122,7 +125,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -138,7 +140,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -154,7 +155,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -170,7 +170,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -186,7 +185,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -202,7 +200,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
|
@ -218,7 +215,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
|
@ -234,7 +230,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -250,7 +245,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -266,7 +260,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -282,7 +275,6 @@
|
|||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -298,7 +290,6 @@
|
|||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -314,7 +305,6 @@
|
|||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -330,7 +320,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -346,7 +335,6 @@
|
|||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -362,7 +350,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -378,7 +365,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
|
@ -394,7 +380,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
|
@ -410,7 +395,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
|
@ -426,7 +410,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -442,7 +425,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -458,7 +440,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -711,7 +692,7 @@
|
|||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
|
@ -725,7 +706,7 @@
|
|||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
|
@ -734,7 +715,7 @@
|
|||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
|
@ -743,7 +724,7 @@
|
|||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
|
@ -758,7 +739,7 @@
|
|||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
|
@ -860,7 +841,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -873,7 +853,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -886,7 +865,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -899,7 +877,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -912,7 +889,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -925,7 +901,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -938,7 +913,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -951,7 +925,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -964,7 +937,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -977,7 +949,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -990,7 +961,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -1003,7 +973,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -1016,7 +985,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -1047,7 +1015,7 @@
|
|||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
|
@ -1072,7 +1040,7 @@
|
|||
"version": "20.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
|
@ -1084,9 +1052,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.21.0",
|
||||
|
@ -1487,15 +1455,28 @@
|
|||
"integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"node_modules/@vuetify/loader-shared": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz",
|
||||
"integrity": "sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
"upath": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0",
|
||||
"vuetify": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz",
|
||||
"integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.20",
|
||||
"@vueuse/metadata": "10.9.0",
|
||||
"@vueuse/shared": "10.9.0",
|
||||
"vue-demi": ">=0.14.7"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
|
@ -1527,19 +1508,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz",
|
||||
"integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz",
|
||||
"integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
"vue-demi": ">=0.14.7"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
|
@ -1574,7 +1555,7 @@
|
|||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
@ -1607,6 +1588,14 @@
|
|||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi_up": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz",
|
||||
"integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
|
@ -1800,7 +1789,7 @@
|
|||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
|
@ -2094,7 +2083,7 @@
|
|||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
|
@ -2117,7 +2106,7 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"detect-libc": "bin/detect-libc.js"
|
||||
},
|
||||
|
@ -2275,9 +2264,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.6.1.tgz",
|
||||
"integrity": "sha512-6VRpLjwtIVdtUuITJPPKtpOH1NM6nuAkRE3q5O4Lrx0N1bYMhTkiqb2Jy7zfQuDPbOIkkF2OABTzegpNnzgsnQ==",
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.7.3.tgz",
|
||||
"integrity": "sha512-OaqY1kQ2xzNyRFyge3fzM7jqMwux+464RBEqd+ybRV9xPiGxtgnj/sVK4iEbnKnzQIa9XK03DOIFzoToUhu1DA==",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
|
@ -2299,6 +2288,94 @@
|
|||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||
"version": "0.14.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
||||
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||
"version": "0.14.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
||||
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
|
@ -2320,7 +2397,7 @@
|
|||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
|
@ -2807,7 +2884,6 @@
|
|||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -3265,7 +3341,7 @@
|
|||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.24.1.tgz",
|
||||
"integrity": "sha512-kUpHOLiH5GB0ERSv4pxqlL0RYKnOXtgGtVe7shDGfhS0AZ4D1ouKFYAcLcZhql8aMspDNzaUCumGHZ78tb2fTg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^1.0.3"
|
||||
},
|
||||
|
@ -3295,7 +3371,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -3315,7 +3390,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -3335,7 +3409,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
|
@ -3355,7 +3428,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -3375,7 +3447,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -3395,7 +3466,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -3415,7 +3485,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -3435,7 +3504,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -3455,7 +3523,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -3659,7 +3726,7 @@
|
|||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/muggle-string": {
|
||||
"version": "0.4.1",
|
||||
|
@ -4480,7 +4547,7 @@
|
|||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
|
||||
"integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
},
|
||||
|
@ -4607,7 +4674,7 @@
|
|||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -4624,7 +4691,7 @@
|
|||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
|
@ -4903,7 +4970,7 @@
|
|||
"version": "5.29.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz",
|
||||
"integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
|
@ -4921,7 +4988,7 @@
|
|||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
|
@ -5033,7 +5100,7 @@
|
|||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/unimport": {
|
||||
"version": "3.7.1",
|
||||
|
@ -5184,6 +5251,16 @@
|
|||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/upath": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
|
||||
"integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"yarn": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
|
@ -5233,7 +5310,7 @@
|
|||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.2.tgz",
|
||||
"integrity": "sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
"postcss": "^8.4.36",
|
||||
|
@ -5351,6 +5428,25 @@
|
|||
"vite": "^5.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-vuetify": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.3.tgz",
|
||||
"integrity": "sha512-HbYajgGgb/noaVKNRhnnXIiQZrNXfNIeanUGAwXgOxL6h/KULS40Uf51Kyz8hNmdegF+DwjgXXI/8J1PNS83xw==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@vuetify/loader-shared": "^2.0.3",
|
||||
"debug": "^4.3.3",
|
||||
"upath": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">=5",
|
||||
"vue": "^3.0.0",
|
||||
"vuetify": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-svg-loader": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz",
|
||||
|
@ -5467,6 +5563,39 @@
|
|||
"typescript": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/vuetify": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.6.5.tgz",
|
||||
"integrity": "sha512-YrHTM1vb7UllAtfH9tWfTo1wYMjyCSybu4WtXrfMRpMwAaZWgfrMmqD/4Tc+0KqDsDsYMXaYs0nJ6HtdMJZbyA==",
|
||||
"engines": {
|
||||
"node": "^12.20 || >=14.13"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/johnleider"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.7",
|
||||
"vite-plugin-vuetify": ">=1.0.0",
|
||||
"vue": "^3.3.0",
|
||||
"vue-i18n": "^9.0.0",
|
||||
"webpack-plugin-vuetify": ">=2.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
},
|
||||
"vite-plugin-vuetify": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack-plugin-vuetify": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": ". ./set_env.sh && vite",
|
||||
"devh": ". ./set_env.sh && vite --host",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": ". ./set_env.sh && vite preview",
|
||||
"build-only": ". ./set_env.sh && vite build",
|
||||
|
@ -14,11 +15,14 @@
|
|||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"element-plus": "^2.6.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"ansi_up": "^6.0.2",
|
||||
"element-plus": "^2.7.3",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.10.2",
|
||||
"vuetify": "^3.6.5",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -44,6 +48,7 @@
|
|||
"vite-plugin-css-injected-by-js": "^3.5.0",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-singlefile": "^2.0.1",
|
||||
"vite-plugin-vuetify": "^2.0.3",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^2.0.6"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import {type ApiJsonMsg} from '@/api'
|
||||
import * as api from "@/api/index";
|
||||
|
||||
export enum WtDataFlowCmd {
|
||||
UNKNOWN = 0,
|
||||
GET_INS_LIST = 1,
|
||||
GET_CUR_INS = 2,
|
||||
GET_CUR_ATTACH_LIST = 3,
|
||||
GET_ATTACH_LIST = 4,
|
||||
ATTACH = 5,
|
||||
ATTACH_CUR_TO_RECVER = 6,
|
||||
ATTACH_CUR_TO_SENDER = 7,
|
||||
DETACH_SINGLE = 8,
|
||||
DETACH_CUR_FROM = 9,
|
||||
SET_DATA_TYPE = 10,
|
||||
}
|
||||
|
||||
export interface IWtDataFlowJsonMsg extends ApiJsonMsg {
|
||||
data_type?: 3 | 4,
|
||||
ins_idx?: number,
|
||||
}
|
||||
|
||||
export function wt_data_flow_get_instance_list() {
|
||||
const jsonMsg: IWtDataFlowJsonMsg = {
|
||||
cmd: WtDataFlowCmd.GET_INS_LIST,
|
||||
module: api.WtModuleID.DATA_FLOW,
|
||||
}
|
||||
api.sendJsonMsg(jsonMsg);
|
||||
}
|
||||
|
||||
export function wt_data_flow_attach_cur_to_sender(instance_index: number) {
|
||||
const jsonMsg: IWtDataFlowJsonMsg = {
|
||||
cmd: WtDataFlowCmd.ATTACH_CUR_TO_SENDER,
|
||||
module: api.WtModuleID.DATA_FLOW,
|
||||
data_type: 3,
|
||||
ins_idx: instance_index,
|
||||
}
|
||||
api.sendJsonMsg(jsonMsg);
|
||||
}
|
||||
|
||||
export function wt_data_flow_get_attach_list() {
|
||||
const jsonMsg: IWtDataFlowJsonMsg = {
|
||||
cmd: WtDataFlowCmd.GET_ATTACH_LIST,
|
||||
module: api.WtModuleID.DATA_FLOW,
|
||||
}
|
||||
api.sendJsonMsg(jsonMsg);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import type {ApiBinaryMsg} from "@/api/binDataDef";
|
||||
import {WtDataType} from "@/api/binDataDef";
|
||||
import {type ApiJsonMsg, sendBinMsg, sendJsonMsg, WtModuleID} from "@/api/index";
|
||||
|
||||
export enum WtUartCmd {
|
||||
UNKNOWN = 0,
|
||||
|
||||
/* UART PERIPHERAL */
|
||||
GET_AVAILABLE_NUMS = 1,
|
||||
GET_BAUD = 4,
|
||||
SET_BAUD = 5,
|
||||
GET_CONFIG = 6, /* data bits, parity and stop bits */
|
||||
SET_CONFIG = 7,
|
||||
GET_FLOW_CTRL, /* flow control function RTS/CTS*/
|
||||
SET_FLOW_CTRL,
|
||||
GET_PINS_NUM, /* not implemented change pinout function */
|
||||
SET_PINS_NUM, /* not implemented */
|
||||
GET_MODE, /* not implemented UART/RS485/IrDA */
|
||||
SET_MODE, /* not implemented UART/RS485/IrDA */
|
||||
|
||||
GET_STATUS = 20, /* is uart enabled and other information */
|
||||
SET_STATUS, /* set specific uart port disable */
|
||||
GET_DATA_TYPE = 22, // 0x03 or 0x04
|
||||
SET_DATA_TYPE = 23, // 0x03 or 0x04
|
||||
}
|
||||
|
||||
enum ANSI_ESCAPE_CODE {
|
||||
REFRESH_WINDOW = '\x1b[7t',
|
||||
CLEAR_WINDOW = '\x1b[2J'
|
||||
}
|
||||
|
||||
export interface IUartConfig {
|
||||
data_bits: 5 | 6 | 7 | 8;
|
||||
parity : 0 | 1 | 2;
|
||||
stop_bits: 1 | 15 | 2;
|
||||
}
|
||||
|
||||
export interface IUartMsgConfig extends ApiJsonMsg, IUartConfig {
|
||||
sub_mod: number;
|
||||
}
|
||||
|
||||
export interface IUartMsgBaud extends ApiJsonMsg {
|
||||
sub_mod: number;
|
||||
baud: number;
|
||||
}
|
||||
|
||||
export function uart_send_msg(payload: Uint8Array, sub_mod: number = 1) {
|
||||
/* hard code uart num for now */
|
||||
const msg: ApiBinaryMsg = {
|
||||
sub_mod: sub_mod,
|
||||
data_type: WtDataType.RAW,
|
||||
module: WtModuleID.UART,
|
||||
payload: payload,
|
||||
}
|
||||
sendBinMsg(msg);
|
||||
}
|
||||
|
||||
export function uart_get_baud(uart_num: number = 1) {
|
||||
const cmd = {
|
||||
cmd: WtUartCmd.GET_BAUD,
|
||||
module: WtModuleID.UART,
|
||||
sub_mod: uart_num,
|
||||
}
|
||||
sendJsonMsg(cmd);
|
||||
}
|
||||
|
||||
export function uart_set_baud(baud: number, uart_num: number = 1) {
|
||||
const cmd: IUartMsgBaud = {
|
||||
cmd: WtUartCmd.SET_BAUD,
|
||||
module: WtModuleID.UART,
|
||||
baud: baud,
|
||||
sub_mod: uart_num,
|
||||
}
|
||||
sendJsonMsg(cmd);
|
||||
}
|
||||
|
||||
export function uart_get_config(uart_num: number = 1) {
|
||||
const cmd = {
|
||||
cmd: WtUartCmd.GET_CONFIG,
|
||||
module: WtModuleID.UART,
|
||||
sub_mod: uart_num,
|
||||
}
|
||||
sendJsonMsg(cmd);
|
||||
}
|
||||
|
||||
export function uart_set_config(uart_config: IUartConfig, uart_num: number = 1) {
|
||||
const cmd: IUartMsgConfig = {
|
||||
cmd: WtUartCmd.SET_CONFIG,
|
||||
module: WtModuleID.UART,
|
||||
sub_mod: uart_num,
|
||||
data_bits: uart_config.data_bits,
|
||||
parity: uart_config.parity,
|
||||
stop_bits: uart_config.stop_bits,
|
||||
}
|
||||
sendJsonMsg(cmd);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="M480-360 280-560h400L480-360Z"/></svg>
|
After Width: | Height: | Size: 127 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="m280-400 200-200 200 200H280Z"/></svg>
|
After Width: | Height: | Size: 127 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="m136-80-56-56 264-264H160v-80h320v320h-80v-184L136-80Zm344-400v-320h80v184l264-264 56 56-264 264h184v80H480Z"/></svg>
|
After Width: | Height: | Size: 206 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="M120-120v-320h80v184l504-504H520v-80h320v320h-80v-184L256-200h184v80H120Z"/></svg>
|
After Width: | Height: | Size: 171 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>
|
After Width: | Height: | Size: 292 B |
|
@ -4,6 +4,8 @@ import '@/assets/page.css'
|
|||
import '@/assets/navigation.css'
|
||||
import 'element-plus/dist/index.css';
|
||||
|
||||
import 'vuetify/styles'
|
||||
import { createVuetify } from 'vuetify'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
@ -17,5 +19,6 @@ const app = createApp(App)
|
|||
app.use(createPinia())
|
||||
app.use(i18n);
|
||||
app.use(router)
|
||||
app.use(createVuetify())
|
||||
|
||||
app.mount('#app')
|
||||
|
|
|
@ -0,0 +1,553 @@
|
|||
import {defineStore} from "pinia";
|
||||
import {computed, type Ref, ref, shallowReactive} from "vue";
|
||||
import {AnsiUp} from 'ansi_up'
|
||||
import {debouncedWatch} from "@vueuse/core";
|
||||
import {type IUartConfig, uart_send_msg} from "@/api/apiUart";
|
||||
|
||||
interface IDataArchive {
|
||||
time: number;
|
||||
isRX: boolean;
|
||||
data: Uint8Array;
|
||||
}
|
||||
|
||||
export interface IDataBuf {
|
||||
time: string;
|
||||
isRX: boolean;
|
||||
data: Uint8Array;
|
||||
str: string;
|
||||
hex: string;
|
||||
hexdump: string;
|
||||
}
|
||||
|
||||
function decodeUtf8(u8Arr: Uint8Array) {
|
||||
try {
|
||||
const decoder = new TextDecoder();
|
||||
const decodedText = decoder.decode(u8Arr); // Attempt to decode
|
||||
return decodedText.replace(/\uFFFD/g, ''); // Remove all <20> characters
|
||||
} catch (error) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHTML(text: string) {
|
||||
const element = document.createElement('p');
|
||||
element.textContent = text;
|
||||
return element.innerHTML;
|
||||
}
|
||||
|
||||
function unescapeString(str: string) {
|
||||
return str.replace(/\\(u[0-9a-fA-F]{4}|x[0-9a-fA-F]{2}|[0-7]{1,3}|.)/g, (_, escapeChar) => {
|
||||
switch (escapeChar[0]) {
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'b':
|
||||
return '\b';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case '0':
|
||||
return '\0';
|
||||
case 'x':
|
||||
return String.fromCharCode(parseInt(escapeChar.slice(1), 16));
|
||||
case 'u':
|
||||
return String.fromCharCode(parseInt(escapeChar.slice(1), 16));
|
||||
default:
|
||||
if (/^[0-7]{1,3}$/.test(escapeChar)) {
|
||||
return String.fromCharCode(parseInt(escapeChar, 8));
|
||||
}
|
||||
return escapeChar;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const zeroPad = (num: number, places: number) => String(num).padStart(places, '0');
|
||||
const ansi_up = new AnsiUp();
|
||||
ansi_up.escape_html = false;
|
||||
|
||||
/* quick HEX lookup table */
|
||||
const byteToHex: string[] = new Array(256);
|
||||
for (let n = 0; n <= 0xff; ++n) {
|
||||
byteToHex[n] = n.toString(16).padStart(2, "0").toUpperCase();
|
||||
}
|
||||
|
||||
function u8toHexString(buff: Uint8Array) {
|
||||
const hexOctets = []; // new Array(buff.length) is even faster (preallocates necessary array size), then use hexOctets[i] instead of .push()
|
||||
if (buff.length === 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
for (let i = 0; i < buff.length; ++i)
|
||||
hexOctets.push(byteToHex[buff[i]]);
|
||||
|
||||
return hexOctets.join(" ");
|
||||
}
|
||||
|
||||
function u8toHexdump(buffer: Uint8Array) {
|
||||
const lines: string[] = [];
|
||||
const bytesPerRow = 16;
|
||||
|
||||
for (let lineStart = 0; lineStart < buffer.length; lineStart += bytesPerRow) {
|
||||
let result = lineStart.toString(16).padStart(4, '0') + ": ";
|
||||
let ascii = "";
|
||||
|
||||
// Process each byte in the row
|
||||
for (let i = 0; i < bytesPerRow; i++) {
|
||||
const byteIndex = lineStart + i;
|
||||
if (byteIndex >= buffer.length) {
|
||||
// Pad the row if it's shorter than the full width
|
||||
result += " ";
|
||||
} else {
|
||||
const byte = buffer[byteIndex];
|
||||
result += byteToHex[byte] + " ";
|
||||
|
||||
// Prepare the ASCII representation, non-printable as '.'
|
||||
if (byte >= 32 && byte <= 126) {
|
||||
ascii += String.fromCharCode(byte);
|
||||
} else {
|
||||
ascii += ".";
|
||||
}
|
||||
}
|
||||
if (i === 8) {
|
||||
result += " "
|
||||
}
|
||||
}
|
||||
|
||||
result += "|" + ascii + "|" + " ".repeat(16 - ascii.length);
|
||||
lines.push(result);
|
||||
}
|
||||
|
||||
return strToHTML(escapeHTML(lines.join('\n')));
|
||||
}
|
||||
|
||||
function strToHTML(str: string) {
|
||||
return str.replace(/\n/g, '<br>') // Replace newline with <br> tag
|
||||
.replace(/\t/g, ' ') // Replace tab with spaces (or you could use ' ' for single spaces)
|
||||
.replace(/ /g, ' ');
|
||||
}
|
||||
|
||||
function isArrayContained(subArray: Uint8Array, mainArray: Uint8Array) {
|
||||
if (subArray.length === 0) return -1;
|
||||
outerLoop: for (let i = 0; i <= mainArray.length - subArray.length; i++) {
|
||||
// Check if subArray is found starting at position i in mainArray
|
||||
for (let j = 0; j < subArray.length; j++) {
|
||||
if (mainArray[i + j] !== subArray[j]) {
|
||||
continue outerLoop; // Continue the outer loop if mismatch found
|
||||
}
|
||||
}
|
||||
return i; // subArray is found within mainArray
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const baudArr = [
|
||||
{
|
||||
start: 300,
|
||||
count: 9,
|
||||
}, {
|
||||
start: 14400,
|
||||
count: 9,
|
||||
}
|
||||
]
|
||||
|
||||
function generateBaudArr(results: { baud: number;}[]) {
|
||||
for (let i = 0; i < baudArr.length; ++i) {
|
||||
let start = baudArr[i].start;
|
||||
for (let j = 0; j < baudArr[i].count; ++j) {
|
||||
results.push({baud: start});
|
||||
start += start;
|
||||
}
|
||||
}
|
||||
|
||||
results.sort((a, b) => {
|
||||
return a.baud - b.baud;
|
||||
});
|
||||
}
|
||||
|
||||
export const useDataViewerStore = defineStore('text-viewer', () => {
|
||||
/* private value */
|
||||
const predefineColors = [
|
||||
'#f0f9eb',
|
||||
'#ecf4ff',
|
||||
'rgba(255, 69, 0, 0.68)',
|
||||
'rgb(255, 120, 0)',
|
||||
'hsv(51, 100, 98)',
|
||||
'hsva(120, 40, 94, 0.5)',
|
||||
'hsl(181, 100%, 37%)',
|
||||
'hsla(209, 100%, 56%, 0.73)',
|
||||
'rgba(85,155,197,0.44)',
|
||||
]
|
||||
|
||||
const predefinedUartBaudFrequent = Object.freeze([
|
||||
{
|
||||
baud: 115200,
|
||||
}, {
|
||||
baud: 921600,
|
||||
}, {
|
||||
baud: 9600,
|
||||
}
|
||||
])
|
||||
|
||||
const uartBaudList: { baud: number; }[] = [];
|
||||
generateBaudArr(uartBaudList);
|
||||
|
||||
/* public value */
|
||||
const configPanelTab = ref("second");
|
||||
const configPanelShow = ref(true);
|
||||
const quickAccessPanelShow = ref(true);
|
||||
const enableAnsiDecode = ref(true);
|
||||
const frameBreakSequence = ref("\\n");
|
||||
const frameBreakSequenceNormalized = ref(new Uint8Array(0));
|
||||
const frameBreakDelay = ref(0);
|
||||
|
||||
const showText = ref(true);
|
||||
const showHex = ref(false);
|
||||
const showHexdump = ref(false);
|
||||
const enableLineWrap = ref(true);
|
||||
const showTimestamp = ref(true);
|
||||
|
||||
const pauseAutoRefresh = ref(false);
|
||||
|
||||
const RxHexdumpColor = ref("#f0f9eb");
|
||||
const RxTotalByteCount = ref(0);
|
||||
const RxByteCount = ref(0);
|
||||
|
||||
const TxHexdumpColor = ref("#ecf4ff");
|
||||
const TxTotalByteCount = ref(0);
|
||||
const TxByteCount = ref(0);
|
||||
|
||||
const enableFilter = ref(true);
|
||||
const enableMatch = ref(false);
|
||||
const forceToBottom = ref(true);
|
||||
const filterChanged = ref(false);
|
||||
|
||||
const textSuffixValue = ref("")
|
||||
const textPrefixValue = ref("")
|
||||
|
||||
const uartBaud = ref(115200);
|
||||
const uartBaudReal = ref(115200);
|
||||
const uartConfig: Ref<IUartConfig> = ref({
|
||||
data_bits: 8,
|
||||
parity: 0,
|
||||
stop_bits: 1,
|
||||
});
|
||||
|
||||
const filterValue = ref("");
|
||||
const computedFilterValue = computed(() => {
|
||||
const str = unescapeString(filterValue.value);
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(str);
|
||||
})
|
||||
|
||||
const computedSuffixValue = computed(() => {
|
||||
const str = unescapeString(textSuffixValue.value);
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(str);
|
||||
})
|
||||
|
||||
const computedPrefixValue = computed(() => {
|
||||
const str = unescapeString(textPrefixValue.value);
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(str);
|
||||
})
|
||||
|
||||
debouncedWatch(() => frameBreakSequence.value, (newValue) => {
|
||||
const unescapedStr = unescapeString(newValue);
|
||||
const encoder = new TextEncoder();
|
||||
frameBreakSequenceNormalized.value = encoder.encode(unescapedStr);
|
||||
}, {debounce: 300, immediate: true});
|
||||
|
||||
debouncedWatch(() => frameBreakDelay.value, (newValue) => {
|
||||
if (newValue < 0) {
|
||||
frameBreakReady = false;
|
||||
clearTimeout(frameBreakTimeoutID);
|
||||
} else if (newValue === 0) {
|
||||
frameBreakReady = true;
|
||||
clearTimeout(frameBreakTimeoutID);
|
||||
} else {
|
||||
refreshTimeout();
|
||||
}
|
||||
}, {debounce: 300, immediate: true});
|
||||
|
||||
const dataArchive: IDataArchive[] = [];
|
||||
const dataBuf: IDataBuf[] = [];
|
||||
const dataBufLength = ref(0);
|
||||
|
||||
/* actual data shown on screen */
|
||||
const dataFiltered: IDataBuf[] = shallowReactive([]);
|
||||
const dataFilteredLength = ref(0);
|
||||
|
||||
|
||||
let frameBreakReady = false;
|
||||
let frameBreakTimeoutID = setTimeout(() => {
|
||||
}, 0);
|
||||
|
||||
debouncedWatch(computedFilterValue, () => {
|
||||
dataFiltered.length = 0; // Clear the array efficiently
|
||||
if (computedFilterValue.value.length === 0) {
|
||||
dataFiltered.push(...dataBuf);
|
||||
filterChanged.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const index = dataFiltered.length;
|
||||
for (const item of dataBuf) {
|
||||
const index = isArrayContained(computedFilterValue.value, item.data);
|
||||
if (index >= 0) {
|
||||
dataFiltered.push(item);
|
||||
}
|
||||
filterChanged.value = true;
|
||||
}
|
||||
}, {debounce: 300});
|
||||
|
||||
function addString(item: string, isRX: boolean = false, doSend: boolean = false) {
|
||||
const encoder = new TextEncoder();
|
||||
item = unescapeString(item);
|
||||
const encodedStr = encoder.encode(item);
|
||||
return addItem(encodedStr, isRX, doSend);
|
||||
}
|
||||
|
||||
function addHexString(item: string, isRX: boolean = false, doSend: boolean = false){
|
||||
if (item === "") {
|
||||
return addItem(new Uint8Array(0), isRX);
|
||||
}
|
||||
const hexArray = item.split(' ');
|
||||
// Map each hex value to a decimal (integer) and create a Uint8Array from these integers
|
||||
const uint8Array = new Uint8Array(hexArray.map(hex => parseInt(hex, 16)));
|
||||
return addItem(uint8Array, isRX, doSend);
|
||||
}
|
||||
|
||||
function addItem(item: Uint8Array, isRX: boolean, doSend: boolean = false){
|
||||
const t = new Date();
|
||||
|
||||
// dataArchive.push({
|
||||
// time: t.getMilliseconds(),
|
||||
// isRX: isRX,
|
||||
// data: u8arr,
|
||||
// });
|
||||
|
||||
if (isRX) {
|
||||
RxTotalByteCount.value += item.length;
|
||||
RxByteCount.value = item.length;
|
||||
} else {
|
||||
/* append prefix and suffix */
|
||||
if (computedPrefixValue.value.length || computedSuffixValue.value.length) {
|
||||
const newArr = new Uint8Array(computedPrefixValue.value.length +
|
||||
computedSuffixValue.value.length + item.length);
|
||||
newArr.set(computedPrefixValue.value);
|
||||
newArr.set(item, computedPrefixValue.value.length);
|
||||
newArr.set(computedSuffixValue.value, computedPrefixValue.value.length + item.length);
|
||||
item = newArr;
|
||||
}
|
||||
if (doSend) {
|
||||
/* INFO: hard coded for the moment */
|
||||
uart_send_msg(item);
|
||||
}
|
||||
TxTotalByteCount.value += item.length;
|
||||
TxByteCount.value = item.length;
|
||||
}
|
||||
|
||||
let str = decodeUtf8(item);
|
||||
str = escapeHTML(str);
|
||||
str = strToHTML(str);
|
||||
|
||||
/* unescape data \n */
|
||||
if (enableAnsiDecode.value) {
|
||||
/* ansi_to_html will escape HTML sequence */
|
||||
str = ansi_up.ansi_to_html("\x1b[0m" + str);
|
||||
}
|
||||
|
||||
dataBuf.push({
|
||||
time: "["
|
||||
+ zeroPad(t.getHours(), 2) + ":"
|
||||
+ zeroPad(t.getMinutes(), 2) + ":"
|
||||
+ zeroPad(t.getSeconds(), 2) + ":"
|
||||
+ zeroPad(t.getMilliseconds(), 3)
|
||||
+ "]",
|
||||
data: item,
|
||||
isRX: isRX,
|
||||
str: str,
|
||||
hex: u8toHexString(item),
|
||||
hexdump: u8toHexdump(item),
|
||||
});
|
||||
|
||||
if (dataBuf.length >= 20000) {
|
||||
dataBuf.splice(0, 5000);
|
||||
}
|
||||
|
||||
if (!enableFilter.value || computedFilterValue.value.length === 0) {
|
||||
dataFiltered.push(dataBuf[dataBuf.length - 1]);
|
||||
if (dataFiltered.length >= 20000) {
|
||||
dataFiltered.splice(0, 5000);
|
||||
}
|
||||
} else if (enableFilter.value && isArrayContained(computedFilterValue.value, dataBuf[dataBuf.length - 1].data) >= 0) {
|
||||
dataFiltered.push(dataBuf[dataBuf.length - 1]);
|
||||
if (dataFiltered.length >= 20000) {
|
||||
dataFiltered.splice(0, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
dataBufLength.value = dataBuf.length;
|
||||
}
|
||||
|
||||
function popItem() {
|
||||
dataBuf.pop();
|
||||
dataFiltered.pop();
|
||||
}
|
||||
|
||||
function doFrameBreak() {
|
||||
frameBreakReady = true;
|
||||
}
|
||||
|
||||
|
||||
function refreshTimeout() {
|
||||
/* always break */
|
||||
// if (frameBreakDelay.value === 0) {
|
||||
// frameBreakReady = true;
|
||||
// }
|
||||
|
||||
|
||||
if (!frameBreakReady && frameBreakDelay.value > 0) {
|
||||
clearTimeout(frameBreakTimeoutID);
|
||||
frameBreakTimeoutID = setTimeout(doFrameBreak, frameBreakDelay.value);
|
||||
}
|
||||
}
|
||||
|
||||
function addChunk(item: Uint8Array, isRX: boolean) {
|
||||
let newArray: Uint8Array;
|
||||
|
||||
if (frameBreakSequence.value === "") {
|
||||
if (frameBreakReady || dataBuf.length === 0 || dataBuf[dataBuf.length - 1].isRX != isRX) {
|
||||
addItem(item, isRX);
|
||||
frameBreakReady = false;
|
||||
} else {
|
||||
/* TODO: append item to last */
|
||||
newArray = new Uint8Array(dataBuf[dataBuf.length - 1].data.length + item.length + 1);
|
||||
newArray.set(dataBuf[dataBuf.length - 1].data);
|
||||
newArray.set(item, dataBuf[dataBuf.length - 1].data.length);
|
||||
popItem();
|
||||
addItem(newArray, isRX);
|
||||
}
|
||||
refreshTimeout();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (frameBreakReady) {
|
||||
newArray = item;
|
||||
} else {
|
||||
if (dataBuf.length) {
|
||||
newArray = new Uint8Array(dataBuf[dataBuf.length - 1].data.length + item.length + 1);
|
||||
newArray.set(dataBuf[dataBuf.length - 1].data);
|
||||
newArray.set(item, dataBuf[dataBuf.length - 1].data.length);
|
||||
popItem();
|
||||
} else {
|
||||
newArray = item;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(newArray)
|
||||
console.log(frameBreakSequenceNormalized.value)
|
||||
|
||||
/* break frame at sequence match */
|
||||
let matchIndex = isArrayContained(frameBreakSequenceNormalized.value, newArray);
|
||||
while (matchIndex < 0) {
|
||||
console.log(matchIndex)
|
||||
/* update last buf item */
|
||||
addItem(newArray.slice(0, matchIndex + frameBreakSequenceNormalized.value.length), isRX);
|
||||
newArray = newArray.slice(matchIndex + frameBreakSequenceNormalized.value.length);
|
||||
matchIndex = isArrayContained(frameBreakSequenceNormalized.value, newArray);
|
||||
}
|
||||
addItem(newArray.slice(0, matchIndex + frameBreakSequenceNormalized.value.length), isRX);
|
||||
}
|
||||
|
||||
function clearByteCount(isRX: boolean) {
|
||||
if (isRX) {
|
||||
RxTotalByteCount.value = 0;
|
||||
} else {
|
||||
TxTotalByteCount.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function clearDataBuff() {
|
||||
dataBuf.length = 0;
|
||||
dataFiltered.length = 0;
|
||||
dataBufLength.value = 0;
|
||||
|
||||
RxByteCount.value = 0;
|
||||
RxTotalByteCount.value = 0;
|
||||
|
||||
TxByteCount.value = 0;
|
||||
TxTotalByteCount.value = 0;
|
||||
}
|
||||
|
||||
function clearFilteredBuff() {
|
||||
dataFiltered.length = 0;
|
||||
}
|
||||
|
||||
function refreshFilteredBuff() {
|
||||
const oldValue = filterValue.value;
|
||||
filterValue.value += "66";
|
||||
filterValue.value = oldValue;
|
||||
}
|
||||
|
||||
function setUartBaud(baud: number) {
|
||||
uartBaudReal.value = baud;
|
||||
for (let i = 0; i < uartBaudList.length; i++) {
|
||||
const difference = Math.abs(uartBaudList[i].baud - baud);
|
||||
const percentageDifference = (difference / baud);
|
||||
if (percentageDifference !== 0 && percentageDifference < 0.001) {
|
||||
uartBaud.value = uartBaudList[i].baud;
|
||||
return;
|
||||
}
|
||||
}
|
||||
uartBaud.value = baud;
|
||||
}
|
||||
|
||||
return {
|
||||
addItem,
|
||||
addChunk,
|
||||
addString,
|
||||
addHexString,
|
||||
clearFilteredBuff,
|
||||
clearDataBuff,
|
||||
refreshFilteredBuff,
|
||||
textSuffixValue,
|
||||
textPrefixValue,
|
||||
clearByteCount,
|
||||
dataBufLength,
|
||||
configPanelTab,
|
||||
configPanelShow,
|
||||
pauseAutoRefresh,
|
||||
quickAccessPanelShow,
|
||||
dataFiltered,
|
||||
filterValue,
|
||||
enableAnsiDecode,
|
||||
showHex,
|
||||
showHexdump,
|
||||
showText,
|
||||
showTimestamp,
|
||||
enableLineWrap,
|
||||
RxHexdumpColor,
|
||||
TxHexdumpColor,
|
||||
predefineColors,
|
||||
RxByteCount,
|
||||
RxTotalByteCount,
|
||||
TxByteCount,
|
||||
TxTotalByteCount,
|
||||
forceToBottom,
|
||||
frameBreakSequence,
|
||||
frameBreakDelay,
|
||||
filterChanged,
|
||||
|
||||
/* UART */
|
||||
predefinedUartBaudFrequent,
|
||||
uartBaudList,
|
||||
uartBaud,
|
||||
uartConfig,
|
||||
uartBaudReal,
|
||||
setUartBaud,
|
||||
}
|
||||
});
|
|
@ -1,9 +1,497 @@
|
|||
<template>
|
||||
<div class="button-m-0 messages-container flex flex-grow overflow-hidden" :class="{'flex-col': layoutMode==='col'}">
|
||||
<div v-show="store.configPanelShow" ref="win1Ref" class="bg-gray-50 flex-shrink-0 overflow-auto"
|
||||
:class="{
|
||||
'max-w-60': layoutMode==='row', 'xl:max-w-80': layoutMode==='row',
|
||||
'min-w-60': layoutMode==='row', 'xl:min-w-80': layoutMode==='row'
|
||||
}"
|
||||
>
|
||||
<text-data-config></text-data-config>
|
||||
</div>
|
||||
|
||||
<div v-show="store.configPanelShow && (winDataView.show || win2.show)" ref="firstWinResizeRef"></div>
|
||||
|
||||
<div v-show="winDataView.show" class="flex flex-col flex-grow overflow-hidden p-2">
|
||||
<textDataViewer></textDataViewer>
|
||||
</div>
|
||||
|
||||
<div v-show="winDataView.show && win2.show" ref="thirdWinResizeRef"></div>
|
||||
|
||||
<div v-show="win2.show" ref="win2Ref" :class="{
|
||||
'max-w-80': layoutMode==='row', 'xl:max-w-96': layoutMode==='row',
|
||||
'min-w-80': layoutMode==='row', 'xl:min-w-96': layoutMode==='row'
|
||||
}"
|
||||
class="bg-gray-50 flex flex-col flex-shrink-0 min-h-32 overflow-auto p-2">
|
||||
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
<el-text type="primary">快捷发送</el-text>
|
||||
<el-text size="small">努力施工中</el-text>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<teleport to="#page-spec-slot">
|
||||
<div>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:hide-after="0"
|
||||
transition="none"
|
||||
>
|
||||
<div class="button-m-0 flex flex-col space-y-2">
|
||||
<div class="custom-style flex justify-center">
|
||||
<el-segmented v-model="layoutMode" :options="layoutOptions" size="small"/>
|
||||
</div>
|
||||
<el-checkbox label="自适应" v-model="layoutConf.isAutoHide" border size="small"
|
||||
:disabled="layoutMode==='col'"/>
|
||||
<el-checkbox label="设置窗" v-model="store.configPanelShow" border size="small" :disabled="layoutConf.isAutoHide"/>
|
||||
<el-checkbox label="数据窗" v-model="winDataView.show" border size="small" :disabled="layoutConf.isAutoHide"/>
|
||||
<el-checkbox label="快捷窗" v-model="win2.show" border size="small" :disabled="layoutConf.isAutoHide"/>
|
||||
</div>
|
||||
|
||||
<template #reference>
|
||||
<el-button class="min-h-full" type="primary" :size="layoutConf.isMedium ? 'small' : 'default'">布局
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="mx-1"></div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted, reactive, type Ref, ref, type UnwrapRef, watch} from "vue";
|
||||
import {breakpointsTailwind, useBreakpoints, useWindowSize} from '@vueuse/core'
|
||||
import {useDataViewerStore} from '@/stores/dataViewerStore';
|
||||
import * as api from '@/api';
|
||||
import * as uart from '@/api/apiUart';
|
||||
import * as bin from '@/api/binDataDef';
|
||||
import * as data_flow from '@/api/apiDataFlow';
|
||||
import textDataViewer from "@/views/text-data-viewer/textDataViewer.vue";
|
||||
import textDataConfig from "@/views/text-data-viewer/textDataConfig.vue"
|
||||
import {registerModule} from "@/router/msgRouter";
|
||||
import {type ApiBinaryMsg} from "@/api/binDataDef";
|
||||
import {
|
||||
type IUartMsgBaud,
|
||||
type IUartMsgConfig,
|
||||
uart_get_baud,
|
||||
uart_get_config,
|
||||
uart_set_baud,
|
||||
uart_set_config,
|
||||
WtUartCmd
|
||||
} from "@/api/apiUart";
|
||||
import {isDevMode} from "@/composables/buildMode";
|
||||
import {ControlEvent} from "@/api";
|
||||
import {wt_data_flow_attach_cur_to_sender} from "@/api/apiDataFlow";
|
||||
|
||||
const store = useDataViewerStore()
|
||||
|
||||
const firstWinResizeRef = ref(document.body);
|
||||
const thirdWinResizeRef = ref(document.body);
|
||||
const win1Ref = ref(document.body);
|
||||
const win2Ref = ref(document.body);
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
const windowSize = useWindowSize()
|
||||
|
||||
const layoutConf = reactive({
|
||||
isSmall: breakpoints.smaller("sm"),
|
||||
isMedium: breakpoints.smaller("lg"),
|
||||
isAutoHide: ref(true)
|
||||
});
|
||||
|
||||
const layoutOptions = [{
|
||||
label: '横/行',
|
||||
value: 'row'
|
||||
}, {
|
||||
label: '竖/列',
|
||||
value: 'col'
|
||||
}]
|
||||
|
||||
const layoutMode = ref(layoutOptions[0].value);
|
||||
|
||||
const displayOptions = [{
|
||||
label: '默认显示',
|
||||
value: 'auto'
|
||||
}, {
|
||||
label: '手动显示',
|
||||
value: 'manual'
|
||||
}]
|
||||
const displayMode = ref(displayOptions[0].value);
|
||||
|
||||
interface WinProperty {
|
||||
show: boolean;
|
||||
width: string;
|
||||
height: string;
|
||||
borderSize: number;
|
||||
}
|
||||
|
||||
const win1 = reactive<WinProperty>({
|
||||
show: true,
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
borderSize: 3,
|
||||
});
|
||||
|
||||
const win2 = reactive<WinProperty>({
|
||||
show: true,
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
borderSize: 3,
|
||||
});
|
||||
|
||||
const winDataView = reactive({
|
||||
show: true,
|
||||
})
|
||||
|
||||
const ctx = reactive({
|
||||
curResizeTarget: "none",
|
||||
curHeightOffset: 0,
|
||||
});
|
||||
|
||||
function updateCursor(i: HTMLElement) {
|
||||
if (layoutMode.value === 'row') {
|
||||
i.style.cursor = "col-resize";
|
||||
} else {
|
||||
i.style.cursor = "row-resize";
|
||||
}
|
||||
}
|
||||
|
||||
function updateWin(r: Ref<HTMLElement>, p: UnwrapRef<WinProperty>) {
|
||||
if (layoutMode.value === 'row') {
|
||||
r.value.style.minHeight = "";
|
||||
r.value.style.maxHeight = ""
|
||||
if (winDataView.show) {
|
||||
r.value.style.minWidth = p.width;
|
||||
r.value.style.maxWidth = p.width;
|
||||
}
|
||||
} else {
|
||||
r.value.style.minWidth = ""
|
||||
r.value.style.maxWidth = ""
|
||||
if (winDataView.show) {
|
||||
r.value.style.minHeight = p.height;
|
||||
r.value.style.maxHeight = p.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateCursors() {
|
||||
updateCursor(firstWinResizeRef.value);
|
||||
updateCursor(thirdWinResizeRef.value);
|
||||
}
|
||||
|
||||
function updateResizer() {
|
||||
updateCursors();
|
||||
updateWin(win1Ref, win1);
|
||||
updateWin(win2Ref, win2);
|
||||
}
|
||||
|
||||
function mouseResize(e: MouseEvent) {
|
||||
const curTarget = e.target as HTMLElement
|
||||
|
||||
if (layoutMode.value === 'row') {
|
||||
let f = e.clientX;
|
||||
if (ctx.curResizeTarget === "first") {
|
||||
win1Ref.value.style.minWidth = f + "px";
|
||||
win1Ref.value.style.maxWidth = f + "px";
|
||||
} else {
|
||||
|
||||
console.log("Row clientX", e.clientX, "clientY", e.clientY,
|
||||
"layerX", e.layerX, "layerY", e.layerY, "offsetX", e.offsetX, "offsetY", e.offsetY,
|
||||
"pageX", e.pageX, "pageY", e.pageY, win2Ref.value.clientHeight);
|
||||
win2Ref.value.style.minWidth = document.body.scrollWidth - f - win2.borderSize + "px";
|
||||
win2Ref.value.style.maxWidth = document.body.scrollWidth - f - win2.borderSize + "px";
|
||||
}
|
||||
} else {
|
||||
/* col mode */
|
||||
let f = e.clientY;
|
||||
if (ctx.curResizeTarget === "first") {
|
||||
win1Ref.value.style.minHeight = f - ctx.curHeightOffset + "px";
|
||||
win1Ref.value.style.maxHeight = f - ctx.curHeightOffset + "px";
|
||||
} else {
|
||||
console.log("Col clientX", e.clientX, "clientY", e.clientY,
|
||||
"layerX", e.layerX, "layerY", e.layerY, "offsetX", e.offsetX, "offsetY", e.offsetY,
|
||||
"pageX", e.pageX, "pageY", e.pageY, curTarget.offsetWidth, ctx.curHeightOffset);
|
||||
win2Ref.value.style.minHeight = ctx.curHeightOffset - f + "px";
|
||||
win2Ref.value.style.maxHeight = ctx.curHeightOffset - f + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function touchResize(e: TouchEvent) {
|
||||
let t = e.touches[0];
|
||||
let f = 0;
|
||||
|
||||
if (layoutMode.value === 'row') {
|
||||
f = t.clientX;
|
||||
if (ctx.curResizeTarget === "first") {
|
||||
win1Ref.value.style.minWidth = f + "px";
|
||||
win1Ref.value.style.maxWidth = f + "px";
|
||||
} else {
|
||||
win2Ref.value.style.minWidth = document.body.scrollWidth - f - win2.borderSize + "px";
|
||||
win2Ref.value.style.maxWidth = document.body.scrollWidth - f - win2.borderSize + "px";
|
||||
}
|
||||
} else {
|
||||
/* column layout mode */
|
||||
f = t.clientY;
|
||||
if (ctx.curResizeTarget === "first") {
|
||||
/* setting window */
|
||||
win1Ref.value.style.minHeight = f - ctx.curHeightOffset + "px";
|
||||
win1Ref.value.style.maxHeight = f - ctx.curHeightOffset + "px";
|
||||
} else {
|
||||
/* quick access window */
|
||||
win2Ref.value.style.minHeight = document.body.scrollHeight - f - win2.borderSize + "px";
|
||||
win2Ref.value.style.maxHeight = document.body.scrollHeight - f - win2.borderSize + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startResize(event: Event) {
|
||||
// Normalize touch and mouse events
|
||||
if (event.type.includes('touch')) {
|
||||
ctx.curHeightOffset = (event as TouchEvent).touches[0].clientY;
|
||||
} else {
|
||||
ctx.curHeightOffset = (event as MouseEvent).clientY;
|
||||
}
|
||||
|
||||
const divRef = event.target;
|
||||
|
||||
if (divRef === firstWinResizeRef.value) {
|
||||
ctx.curResizeTarget = "first";
|
||||
ctx.curHeightOffset -= win1Ref.value.clientHeight;
|
||||
// ctx.curOffset = win1Ref.value.clientHeight;
|
||||
} else if (divRef === thirdWinResizeRef.value) {
|
||||
ctx.curResizeTarget = "third";
|
||||
ctx.curHeightOffset += win2Ref.value.clientHeight;
|
||||
}
|
||||
|
||||
win1Ref.value.style.transition = 'initial';
|
||||
win2Ref.value.style.transition = 'initial';
|
||||
document.addEventListener("mousemove", mouseResize, false);
|
||||
document.addEventListener("touchmove", touchResize, false);
|
||||
layoutConf.isAutoHide = false;
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
if (win1Ref.value) {
|
||||
win1Ref.value.style.transition = '';
|
||||
if (layoutMode.value === "row") {
|
||||
win1.width = win1Ref.value.style.minWidth;
|
||||
} else {
|
||||
win1.height = win1Ref.value.style.minHeight;
|
||||
}
|
||||
}
|
||||
if (win2Ref.value) {
|
||||
win2Ref.value.style.transition = '';
|
||||
if (layoutMode.value === "row") {
|
||||
win2.width = win1Ref.value.style.minWidth;
|
||||
} else {
|
||||
win2.height = win1Ref.value.style.minHeight;
|
||||
}
|
||||
}
|
||||
document.body.style.cursor = '';
|
||||
document.removeEventListener("mousemove", mouseResize, false);
|
||||
document.removeEventListener("touchmove", touchResize, false);
|
||||
}
|
||||
|
||||
watch(() => layoutMode.value, (value) => {
|
||||
updateResizer();
|
||||
if (value === "col") {
|
||||
layoutConf.isAutoHide = false;
|
||||
}
|
||||
});
|
||||
|
||||
watch([
|
||||
() => layoutConf.isSmall,
|
||||
() => layoutConf.isAutoHide
|
||||
], (value) => {
|
||||
if (layoutConf.isAutoHide) {
|
||||
win2.show = !value[0];
|
||||
win1Ref.value.style.minWidth = "";
|
||||
win1Ref.value.style.maxWidth = "";
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
watch([
|
||||
() => layoutConf.isMedium,
|
||||
() => layoutConf.isAutoHide
|
||||
], (value) => {
|
||||
if (layoutConf.isAutoHide) {
|
||||
store.configPanelShow = !value[0];
|
||||
win1Ref.value.style.minWidth = "";
|
||||
win1Ref.value.style.maxWidth = "";
|
||||
win2Ref.value.style.minWidth = "";
|
||||
win2Ref.value.style.maxWidth = "";
|
||||
winDataView.show = true;
|
||||
}
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
watch(() => winDataView.show, value => {
|
||||
if (!value) {
|
||||
win1Ref.value.style.minWidth = "";
|
||||
win1Ref.value.style.maxWidth = "";
|
||||
win1Ref.value.style.maxHeight = "";
|
||||
win1Ref.value.style.maxHeight = "";
|
||||
|
||||
win2Ref.value.style.minWidth = "";
|
||||
win2Ref.value.style.maxWidth = "";
|
||||
win2Ref.value.style.maxHeight = "";
|
||||
win2Ref.value.style.maxHeight = "";
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => win2.show, value => {
|
||||
if (!value && !winDataView.show) {
|
||||
win1Ref.value.style.maxHeight = "";
|
||||
win1Ref.value.style.maxHeight = "";
|
||||
win1Ref.value.style.maxWidth = "";
|
||||
win1Ref.value.style.maxWidth = "";
|
||||
}
|
||||
});
|
||||
|
||||
const onUartJsonMsg = (msg: api.ApiJsonMsg) => {
|
||||
switch (msg.cmd as WtUartCmd) {
|
||||
case WtUartCmd.GET_BAUD:
|
||||
case WtUartCmd.SET_BAUD:{
|
||||
const uartMsg = msg as IUartMsgBaud;
|
||||
if (uartMsg.baud) {
|
||||
store.setUartBaud(uartMsg.baud)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WtUartCmd.GET_CONFIG:
|
||||
case WtUartCmd.SET_CONFIG:{
|
||||
const uartMsg = msg as IUartMsgConfig;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (isDevMode()) {
|
||||
console.log("uart not treated", msg);
|
||||
}
|
||||
break
|
||||
}
|
||||
};
|
||||
|
||||
const onUartBinaryMsg = (msg: ApiBinaryMsg) => {
|
||||
console.log("uart", msg);
|
||||
|
||||
if (msg.sub_mod !== 1) {
|
||||
/* ignore other num for the moment */
|
||||
return;
|
||||
}
|
||||
|
||||
/* UART_NUM_1 msg */
|
||||
store.addItem(new Uint8Array(msg.payload), true);
|
||||
};
|
||||
|
||||
const onDataFlowJsonMsg = (msg: api.ApiJsonMsg) => {
|
||||
if (isDevMode()) {
|
||||
console.log("Dflow Json", msg);
|
||||
}
|
||||
};
|
||||
|
||||
const onDataFlowBinaryMsg = (msg: ApiBinaryMsg) => {
|
||||
if (isDevMode()) {
|
||||
console.log("Dflow Bin", msg);
|
||||
}
|
||||
};
|
||||
|
||||
const onClientCtrl = (msg: api.ControlMsg) => {
|
||||
if (msg.type !== api.ControlMsgType.WS_EVENT) {
|
||||
return
|
||||
}
|
||||
|
||||
if (msg.data === ControlEvent.DISCONNECTED) {
|
||||
|
||||
} else if (msg.data === ControlEvent.CONNECTED) {
|
||||
updateUartData();
|
||||
}
|
||||
};
|
||||
|
||||
function updateUartData() {
|
||||
/* TODO: hard code for the moment, 0 is UART instance id (can be changed in the future) */
|
||||
wt_data_flow_attach_cur_to_sender(0);
|
||||
uart_get_baud();
|
||||
uart_get_config();
|
||||
}
|
||||
|
||||
watch(() => store.uartBaud, value => {
|
||||
uart_set_baud(value);
|
||||
});
|
||||
|
||||
watch(() => store.uartConfig, value => {
|
||||
uart_set_config(value);
|
||||
}, {deep: true});
|
||||
|
||||
onMounted(() => {
|
||||
registerModule(api.WtModuleID.UART, {
|
||||
ctrlCallback: onClientCtrl,
|
||||
serverJsonMsgCallback: onUartJsonMsg,
|
||||
serverBinMsgCallback: onUartBinaryMsg,
|
||||
});
|
||||
|
||||
registerModule(api.WtModuleID.DATA_FLOW, {
|
||||
ctrlCallback: () => {},
|
||||
serverJsonMsgCallback: onDataFlowJsonMsg,
|
||||
serverBinMsgCallback: onDataFlowBinaryMsg,
|
||||
});
|
||||
|
||||
firstWinResizeRef.value.style.borderWidth = win1.borderSize + "px";
|
||||
thirdWinResizeRef.value.style.borderWidth = win2.borderSize + "px";
|
||||
updateCursors()
|
||||
|
||||
if (firstWinResizeRef.value) {
|
||||
firstWinResizeRef.value.addEventListener("mousedown", startResize, false);
|
||||
firstWinResizeRef.value.addEventListener("touchstart", startResize, false);
|
||||
}
|
||||
if (thirdWinResizeRef.value) {
|
||||
thirdWinResizeRef.value.addEventListener("mousedown", startResize, false);
|
||||
thirdWinResizeRef.value.addEventListener("touchstart", startResize, false);
|
||||
}
|
||||
|
||||
document.addEventListener("mouseup", stopResize, false);
|
||||
document.addEventListener("touchend", stopResize, false);
|
||||
updateUartData();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (firstWinResizeRef.value) {
|
||||
firstWinResizeRef.value.removeEventListener("mousedown", startResize, false);
|
||||
firstWinResizeRef.value.removeEventListener("touchstart", startResize, false);
|
||||
}
|
||||
|
||||
if (thirdWinResizeRef.value) {
|
||||
thirdWinResizeRef.value.removeEventListener("mousedown", startResize, false);
|
||||
thirdWinResizeRef.value.removeEventListener("touchstart", startResize, false);
|
||||
}
|
||||
|
||||
document.removeEventListener("mouseup", stopResize, false);
|
||||
document.removeEventListener("touchend", stopResize, false);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-layout">
|
||||
<h2 class="page-title opacity-10">尽请期待</h2>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.button-m-0 :deep(.el-button + .el-button) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.custom-style .el-segmented {
|
||||
--el-segmented-item-selected-color: var(--el-text-color-primary);
|
||||
--el-segmented-item-selected-bg-color: var(--el-color-primary);
|
||||
--el-border-radius-base: 16px;
|
||||
}
|
||||
|
||||
.button-m-0 :deep(.el-checkbox) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="store.configPanelTab" class="mx-2 custom-tabs fit">
|
||||
<el-tab-pane label="接口" name="first" class="min-h-80">
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<el-form :size="store.configPanelShow ? '' : 'small'">
|
||||
<el-form-item
|
||||
label="波特率"
|
||||
class="mb-2"
|
||||
>
|
||||
<div class="flex w-full">
|
||||
<el-select v-model="store.uartBaud" :teleported="false">
|
||||
<template #header>
|
||||
<div class="overflow-auto max-h-40">
|
||||
<div class="flex gap-0">
|
||||
<el-input-number
|
||||
v-model="uartCustomBaud"
|
||||
placeholder="自定义波特率"
|
||||
size="small"
|
||||
:controls="false"
|
||||
:min="110"
|
||||
class="flex-grow"
|
||||
></el-input-number>
|
||||
<el-button size="small" @click="onUseCustomUartBaud">使用</el-button>
|
||||
<!-- <el-button size="small" @click="onConfirm" class="ml-0">增加</el-button>-->
|
||||
</div>
|
||||
|
||||
<el-option-group label="常用">
|
||||
<el-option
|
||||
v-for="item in store.predefinedUartBaudFrequent"
|
||||
:key="item.baud"
|
||||
:value="item.baud"
|
||||
class="border-b list-none"
|
||||
/>
|
||||
</el-option-group>
|
||||
|
||||
<el-option-group label="其他">
|
||||
<el-option
|
||||
v-for="item in store.uartBaudList"
|
||||
:key="item.baud"
|
||||
:value="item.baud"
|
||||
class="border-b list-none"
|
||||
/>
|
||||
</el-option-group>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<p class="text-xs">实际波特率:{{store.uartBaudReal}}</p>
|
||||
|
||||
<el-form-item label="数据位" class="mb-2">
|
||||
<el-select v-model="store.uartConfig.data_bits" :teleported="false" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in uartDataBitsOptions"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="校验位" class="mb-2">
|
||||
<el-select v-model="store.uartConfig.parity" :teleported="false" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in uartParityOptions"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="停止位">
|
||||
<el-select v-model="store.uartConfig.stop_bits" :teleported="false" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in uartStopBitsOptions"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label=" ">
|
||||
<div class="flex">
|
||||
<el-button type="primary">连接</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
|
||||
</el-tab-pane>
|
||||
<!-- ///////////////////////////////////////////////////////////////// -->
|
||||
<el-tab-pane label="显示框" name="second">
|
||||
<div class="flex flex-col">
|
||||
<el-collapse v-model="collapseActiveName">
|
||||
<el-collapse-item name="1">
|
||||
<template #title>
|
||||
显示选项
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox border v-model="store.showText" label="显示文本"/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox border v-model="store.showHex" label="显示HEX"/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox border v-model="store.showHexdump" label="显示HEXDUMP"/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox border v-model="store.showTimestamp">显示时间戳</el-checkbox>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox border v-model="store.enableLineWrap" label="启用换行"/>
|
||||
</div>
|
||||
|
||||
<el-tag type="success">
|
||||
<el-text type="success">RX HEXDUMP高亮选色</el-text>
|
||||
<el-color-picker v-model="store.RxHexdumpColor" show-alpha :predefine="store.predefineColors"
|
||||
size="small"/>
|
||||
</el-tag>
|
||||
|
||||
<el-tag type="primary">
|
||||
<el-text type="primary">TX HEXDUMP高亮选色</el-text>
|
||||
<el-color-picker v-model="store.TxHexdumpColor" show-alpha :predefine="store.predefineColors"
|
||||
size="small"/>
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item name="2">
|
||||
<template #title>
|
||||
其他
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="flex flex-col gap-2">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
placement="right-start"
|
||||
>
|
||||
<template #content>
|
||||
<p>ANSI转义码对终端和文本有很多作用,比如改变文本颜色等。</p>
|
||||
<p>
|
||||
简单了解->
|
||||
<el-link target="_blank" href="https://zhuanlan.zhihu.com/p/390666800">
|
||||
https://zhuanlan.zhihu.com/p/390666800
|
||||
</el-link>
|
||||
</p>
|
||||
</template>
|
||||
<el-checkbox border v-model="store.enableAnsiDecode">解析ANSI转义码</el-checkbox>
|
||||
</el-tooltip>
|
||||
<el-input v-model="store.filterValue" placeholder="文本;支持\n\x" clearable>
|
||||
<template #prepend>
|
||||
过滤
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
|
||||
<!-- <div class="flex flex-col">-->
|
||||
<!-- <el-text type="success">断帧设置</el-text>-->
|
||||
<!-- <el-input v-model="store.frameBreakSequence" class="max-w-52">-->
|
||||
<!-- <template #prepend>-->
|
||||
<!-- 文本匹配断帧-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <el-input v-model="store.frameBreakDelay" type="number" class="max-w-52">-->
|
||||
<!-- <template #prepend>-->
|
||||
<!-- 超时断帧-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- </div>-->
|
||||
|
||||
|
||||
<!-- <div class="flex flex-col flex-wrap">-->
|
||||
<!-- <el-button size="small">滚动到底</el-button>-->
|
||||
|
||||
|
||||
<!-- <div>显示-->
|
||||
<!-- <el-checkbox size="small" border>数据差异高亮</el-checkbox>-->
|
||||
<!-- <el-checkbox size="small" border>TX高亮</el-checkbox>-->
|
||||
<!-- <el-checkbox size="small" border>显示RX</el-checkbox>-->
|
||||
<!-- <el-checkbox size="small" border>显示TX</el-checkbox>-->
|
||||
<!-- <el-checkbox size="small" border>RX右对齐</el-checkbox>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- <!– <div>专有协议–>-->
|
||||
<!-- <!– <el-button size="small">输入格式</el-button>–>-->
|
||||
<!-- <!– <el-button size="small">输出格式</el-button>–>-->
|
||||
<!-- <!– </div>–>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ///////////////////////////////////////////////////////////// -->
|
||||
<el-tab-pane label="发送" name="third">
|
||||
<div class="flex flex-col gap-2">
|
||||
<el-input v-model="store.textPrefixValue" placeholder="支持\n\x" clearable>
|
||||
<template #prepend>
|
||||
{{ `添加帧头►` }}
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input v-model="store.textSuffixValue" placeholder="支持\n\x" clearable>
|
||||
<template #append>
|
||||
◄添加帧尾
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
import {useDataViewerStore} from "@/stores/dataViewerStore";
|
||||
import {globalNotify} from "@/composables/notification";
|
||||
|
||||
const store = useDataViewerStore()
|
||||
const collapseActiveName = ref(["1", "2"])
|
||||
|
||||
const uartCustomBaud = ref(9600)
|
||||
|
||||
const uartDataBitsOptions = [
|
||||
{
|
||||
key: 5,
|
||||
label: "5 bits",
|
||||
}, {
|
||||
key: 6,
|
||||
label: "6 bits",
|
||||
}, {
|
||||
key: 7,
|
||||
label: "7 bits",
|
||||
}, {
|
||||
key: 8,
|
||||
label: "8 bits",
|
||||
}
|
||||
]
|
||||
|
||||
const uartParityOptions = [
|
||||
{
|
||||
key: 0,
|
||||
label: "无(none)",
|
||||
}, {
|
||||
key: 1,
|
||||
label: "奇(odd)",
|
||||
}, {
|
||||
key: 2,
|
||||
label: "偶(even)",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const uartStopBitsOptions = [
|
||||
{
|
||||
key: 1,
|
||||
label: "1",
|
||||
}, {
|
||||
key: 15,
|
||||
label: "1.5",
|
||||
}, {
|
||||
key: 2,
|
||||
label: "2",
|
||||
}
|
||||
]
|
||||
|
||||
const onUseCustomUartBaud = () => {
|
||||
if (uartCustomBaud.value) {
|
||||
store.uartBaud = uartCustomBaud.value;
|
||||
} else {
|
||||
globalNotify("波特率格式错误", "warning")
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-tabs {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.custom-tabs :deep(.el-tabs__item.is-top) {
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
.custom-tabs :deep(.el-tabs__nav.is-top) {
|
||||
@apply w-full flex justify-around
|
||||
}
|
||||
|
||||
.custom-tabs :deep(.el-collapse-item__wrap) {
|
||||
transition: all 0s; /* Customize the duration and easing */
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p></p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,376 @@
|
|||
<template>
|
||||
<div class="flex min-h-7 overflow-auto">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:hide-after="0"
|
||||
transition="none"
|
||||
width="300"
|
||||
>
|
||||
<div v-if="!store.configPanelShow" class="h-[40vh] overflow-auto">
|
||||
<text-data-config></text-data-config>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-link v-show="!store.configPanelShow" type="primary">
|
||||
<InlineSvg name="arrow_drop_down" class="h-6 mb-1 px-2"></InlineSvg>
|
||||
</el-link>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
<div class="flex">
|
||||
<el-checkbox size="small" v-model="store.forceToBottom" label="强制滚动至底部" border/>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<template #content>
|
||||
<p>仅清除显示区域,可用刷新恢复</p>
|
||||
</template>
|
||||
<el-button size="small" @click="store.clearFilteredBuff">
|
||||
<InlineSvg class="h-5" name="trash"></InlineSvg>
|
||||
清屏 ⇩
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<template #content>
|
||||
<p>与缓存同步</p>
|
||||
</template>
|
||||
<el-button size="small" @click="store.refreshFilteredBuff">
|
||||
刷新
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- <el-checkbox class="hover:bg-blue-200" size="small" v-model="store.pauseAutoRefresh" label="暂停数据刷新" border/>-->
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex">
|
||||
<el-button size="small" @click="addItem(1)">add1</el-button>
|
||||
<el-button size="small" @click="addItem(10)">add10</el-button>
|
||||
<el-button size="small" @click="addItem(100)">add100</el-button>
|
||||
<el-button size="small" @click="addItem(1000)">add1000</el-button>
|
||||
<el-button size="small" @click="scrollToBottom">scrollToBottom</el-button>
|
||||
<!-- <el-button @click="toggleAutoBottom">autoBot: {{ forceToBottom }}</el-button>-->
|
||||
<!-- <el-checkbox size="small" v-model="store.forceToBottom" :label="'autoBot:' + store.forceToBottom" border></el-checkbox>-->
|
||||
<!-- <el-button>{{ count }}, {{ items.length }}, {{ vuetifyVirtualScrollBarRef.scrollTop }},-->
|
||||
<!-- {{ vuetifyVirtualScrollBarRef.clientHeight }}, {{ vuetifyVirtualScrollBarRef.scrollHeight }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button @click="updateScroll">{{ scrollTop }}, {{ clientHeight }}, {{ scrollHeight }}</el-button>-->
|
||||
</div>
|
||||
|
||||
<!-- <div>-->
|
||||
<!-- <el-popover-->
|
||||
<!-- placement="bottom"-->
|
||||
<!-- trigger="click"-->
|
||||
<!-- :hide-after="0"-->
|
||||
<!-- transition="none"-->
|
||||
<!-- width="300"-->
|
||||
<!-- >-->
|
||||
<!-- <div id="quick-access-panel-tp" class="bg-amber-200"></div>-->
|
||||
<!-- <template #reference>-->
|
||||
<!-- <el-link class="content-center" v-show="!store.quickAccessPanelShow">-->
|
||||
<!-- <InlineSvg name="arrow_drop_down" class="h-7"></InlineSvg>-->
|
||||
<!-- 666-->
|
||||
<!-- </el-link>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-popover>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
|
||||
<div class="flex flex-grow overflow-hidden border-2 scroll-m-2">
|
||||
<v-virtual-scroll
|
||||
:items="store.dataFiltered"
|
||||
id="myScrollerID"
|
||||
ref="vuetifyVirtualScrollRef"
|
||||
class="font-mono break-all text-sm"
|
||||
:class="[store.enableLineWrap ? 'break-all' : 'text-nowrap']"
|
||||
>
|
||||
<template v-slot:default="{ item, }">
|
||||
<div>
|
||||
<div class="flex">
|
||||
<p class="text-nowrap text-sm text-lime-500" v-if="item.isRX" type="success" v-show="store.showTimestamp"><span>{{ item.time }}</span>◄-RX|</p>
|
||||
<p class="text-nowrap text-sm text-sky-500" v-else type="primary" v-show="store.showTimestamp"><span>{{ item.time }}</span>TX-►|</p>
|
||||
<p v-show="store.showText"
|
||||
v-html="item.str"></p>
|
||||
</div>
|
||||
<div class="flex text-wrap">
|
||||
<p v-show="store.showHex" class="">{{ item.hex }}</p>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<p v-show="store.showHexdump"
|
||||
class="text-nowrap"
|
||||
:style="{ 'background-color': item.isRX ? store.RxHexdumpColor : store.TxHexdumpColor }"
|
||||
v-html="item.hexdump"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 min-h-6 flex gap-2 justify-between overflow-y-scroll">
|
||||
<div class="flex gap-2">
|
||||
<el-link @click="clearSendInput">
|
||||
<el-tag class="font-mono" size="small">
|
||||
<div class="flex ">
|
||||
<InlineSvg class="h-5" name="trash"></InlineSvg>
|
||||
<span class="content-end text-xs">⇩</span>
|
||||
</div>
|
||||
</el-tag>
|
||||
</el-link>
|
||||
|
||||
<div class="flex align-center">
|
||||
<el-checkbox v-model="enableLoopSend" class="font-mono font-bold max-h-5" size="small" border>
|
||||
循环发送(ms)
|
||||
</el-checkbox>
|
||||
<el-input-number
|
||||
v-model="loopSendFreq"
|
||||
class="h-5"
|
||||
size="small"
|
||||
:step="10"
|
||||
>
|
||||
</el-input-number>
|
||||
</div>
|
||||
|
||||
<el-link @click="isSendTextFormat = !isSendTextFormat">
|
||||
<el-tag class="font-mono font-bold" size="small">发送格式:{{ isSendTextFormat ? "文本" : "HEX" }}</el-tag>
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<el-link @click="showTxTotalByte = !showTxTotalByte">
|
||||
<el-tag class="font-mono font-bold" size="small">
|
||||
{{ showTxTotalByte ? `TX统计:${store.TxTotalByteCount}B`: `上个TX帧:${store.TxByteCount}B` }}
|
||||
</el-tag>
|
||||
</el-link>
|
||||
<el-link type="success" @click="showRxTotalByte = !showRxTotalByte">
|
||||
<el-tag class="font-mono font-bold" size="small" type="success">
|
||||
{{ showRxTotalByte ? `RX统计:${store.RxTotalByteCount}B`: `上个RX帧:${store.RxByteCount}B` }}
|
||||
</el-tag>
|
||||
</el-link>
|
||||
<div class="flex align-center">
|
||||
<el-tag class="font-mono font-bold" size="small" type="info">
|
||||
<el-link class="flex" @click="store.clearDataBuff" type="warning">
|
||||
<InlineSvg class="h-5" name="trash"></InlineSvg>
|
||||
</el-link>
|
||||
<span class="align-text-bottom">缓存帧数: {{ store.dataBufLength }}/20000</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row font-mono">
|
||||
<el-input type="textarea" :autosize="{ minRows: 1, maxRows: 6}" v-model="uartInputTextBox" clearable
|
||||
:placeholder="isSendTextFormat ?
|
||||
'输入文本,支持\\n\\x转义' :
|
||||
'输入HEX格式'"
|
||||
@keydown="handleTextboxKeydown"
|
||||
></el-input>
|
||||
<el-tooltip content="Ctrl+回车" placement="top" :auto-close="500">
|
||||
<el-button type="primary"
|
||||
@click="onSendClick">
|
||||
{{ (isSendTextFormat || isHexStringValid) ? "发送" : "格式化" }}
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {useDataViewerStore} from "@/stores/dataViewerStore";
|
||||
import InlineSvg from "@/components/InlineSvg.vue";
|
||||
import TextDataConfig from "@/views/text-data-viewer/textDataConfig.vue";
|
||||
|
||||
const count = ref(0);
|
||||
const showTxTotalByte = ref(false);
|
||||
const showRxTotalByte = ref(false);
|
||||
const vuetifyVirtualScrollRef = ref(document.body);
|
||||
const vuetifyVirtualScrollBarRef = ref(document.body);
|
||||
const vuetifyVirtualScrollContainerRef = ref(document.body);
|
||||
|
||||
const enableLoopSend = ref(false);
|
||||
const loopSendFreq = ref(1000);
|
||||
let loopSendIntervalID: number;
|
||||
const isSendTextFormat = ref(true)
|
||||
const isHexStringValid = ref(false);
|
||||
|
||||
const uartInputTextBox = ref("")
|
||||
const store = useDataViewerStore();
|
||||
|
||||
const mutationObserver = new MutationObserver(() => {
|
||||
if (store.forceToBottom) {
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const parent = document.getElementById('myScrollerID') || document.body;
|
||||
|
||||
// used to scroll to bottom
|
||||
vuetifyVirtualScrollBarRef.value = parent || document.body;
|
||||
|
||||
// used to monitor height changes, so that one new Items are rendered, the height change -> scroll to bottom
|
||||
vuetifyVirtualScrollContainerRef.value = parent.querySelector('.v-virtual-scroll__container') || document.body;
|
||||
|
||||
vuetifyVirtualScrollBarRef.value.onscroll = handleScroll;
|
||||
|
||||
if (vuetifyVirtualScrollContainerRef.value) {
|
||||
const config = { childList: true, subtree: true, attributes: true };
|
||||
mutationObserver.observe(vuetifyVirtualScrollBarRef.value, config)
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
mutationObserver.disconnect();
|
||||
});
|
||||
|
||||
|
||||
function addItem(nr: number) {
|
||||
let rawText = "";
|
||||
|
||||
let maxcount = count.value + nr;
|
||||
|
||||
for (; count.value < maxcount; count.value++) {
|
||||
let text = "";
|
||||
if ((count.value & 3) === 3) {
|
||||
text = `<p class="border-4">${count.value}inputasdf<br/>${count.value}asdf <br/>${count.value}asdfasdf <br/>${count.value}asdf </p>`;
|
||||
text += text;
|
||||
|
||||
} else if ((count.value & 2) === 2) {
|
||||
text = `<p class="border-4">${count.value}inputas df2<br/>${count.value} asdf asd <br/>${count.value}fasdf asdf </p>`;
|
||||
text += text;
|
||||
} else if ((count.value & 1) === 1) {
|
||||
text = `<p class="border-4">${count.value}inputas df2asdf asd <br/>${count.value}fasdf asdf ${count.value} </p>`;
|
||||
text += text;
|
||||
} else {
|
||||
text = `<p class="border-4">${count.value}inputasa<br/>jdhfklasjdhfklasdhflasidfhilasdfhlasdiufhlasdkfhuasnlfcyerhfcibnkuaweghnfctiklaweuyrchnlaweirtucgnawertkcgyawertcnawelcrvnawgervcawencrgf${count.value} </p>`;
|
||||
}
|
||||
|
||||
rawText = count.value + "<p class=\"border-4\"> 666666666b\n6666 666\x1b[33m6666666666666666666666666</p>b\n"
|
||||
const encoder = new TextEncoder();
|
||||
const arr = encoder.encode(rawText);
|
||||
store.addItem(arr, true);
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
nextTick(() => {
|
||||
const scrollerElement = vuetifyVirtualScrollBarRef.value; // Adjust according to your setup
|
||||
// scrollerElement.scrollTop = scrollerElement.scrollHeight;
|
||||
scrollerElement.scrollTo(scrollerElement.scrollLeft, scrollerElement.scrollHeight);
|
||||
});
|
||||
}
|
||||
|
||||
function scrollToTop() {
|
||||
nextTick(() => {
|
||||
vuetifyVirtualScrollBarRef.value.scrollTop = vuetifyVirtualScrollBarRef.value.scrollHeight;
|
||||
// vuetifyVirtualScrollBarRef.value.scrollTo(0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function formatHexInput(input: string) {
|
||||
// Split the input string on spaces to process each segment separately
|
||||
let str;
|
||||
|
||||
// Remove any "0x" prefix and handle uppercase conversion
|
||||
str = input.replace(/^0x/i, ' ').toUpperCase();
|
||||
|
||||
// Remove any non-hexadecimal characters
|
||||
str = str.replace(/[^0-9A-F]/gi, ' ');
|
||||
|
||||
let segments = str.split(/\s+/);
|
||||
let output:string[] = [];
|
||||
|
||||
segments.forEach(segment => {
|
||||
// Check if segment length is odd and needs padding
|
||||
if (segment.length % 2 !== 0) {
|
||||
segment = '0' + segment; // Prepend '0' to make the length even
|
||||
}
|
||||
|
||||
// Split segment into array of two-character chunks
|
||||
let chunked = [];
|
||||
for (let i = 0; i < segment.length; i += 2) {
|
||||
chunked.push(segment.substring(i, i + 2));
|
||||
}
|
||||
|
||||
// Concatenate chunked segments and add to output
|
||||
output.push(chunked.join(' '));
|
||||
});
|
||||
|
||||
// Join all processed segments with a space and return
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
function checkHexTextValid() {
|
||||
isHexStringValid.value = uartInputTextBox.value.toUpperCase() === formatHexInput(uartInputTextBox.value);
|
||||
}
|
||||
|
||||
watch(isSendTextFormat, (value) => {
|
||||
if (!value) {
|
||||
checkHexTextValid()
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => uartInputTextBox.value, (newValue) => {
|
||||
if (!isSendTextFormat.value) {
|
||||
checkHexTextValid()
|
||||
}
|
||||
})
|
||||
|
||||
watch(enableLoopSend, (newValue) => {
|
||||
if (newValue) {
|
||||
clearInterval(loopSendIntervalID);
|
||||
loopSendIntervalID = setInterval(onSendClick, loopSendFreq.value);
|
||||
} else {
|
||||
clearInterval(loopSendIntervalID);
|
||||
}
|
||||
});
|
||||
|
||||
watch(loopSendFreq, (value) => {
|
||||
if (enableLoopSend.value && value) {
|
||||
/* update interval with new value */
|
||||
clearInterval(loopSendIntervalID);
|
||||
loopSendIntervalID = setInterval(onSendClick, loopSendFreq.value);
|
||||
}
|
||||
})
|
||||
|
||||
/* patch scroll container does not update clear filter */
|
||||
watch(() => store.filterChanged, (value) => {
|
||||
if (value) {
|
||||
scrollToBottom();
|
||||
scrollToTop()
|
||||
store.filterChanged = false;
|
||||
}
|
||||
})
|
||||
|
||||
const handleScroll = (ev: Event) => {
|
||||
if (store.forceToBottom) {
|
||||
setTimeout(scrollToBottom, 0);
|
||||
}
|
||||
};
|
||||
|
||||
function clearSendInput() {
|
||||
uartInputTextBox.value = ""
|
||||
}
|
||||
|
||||
function handleTextboxKeydown(ev: KeyboardEvent) {
|
||||
if (ev.ctrlKey && ev.key === 'Enter') {
|
||||
onSendClick();
|
||||
}
|
||||
}
|
||||
|
||||
function onSendClick() {
|
||||
if (isSendTextFormat.value) {
|
||||
store.addString(uartInputTextBox.value, false, true);
|
||||
} else if (!isHexStringValid.value) {
|
||||
uartInputTextBox.value = formatHexInput(uartInputTextBox.value);
|
||||
} else {
|
||||
store.addHexString(uartInputTextBox.value, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -7,7 +7,7 @@ import vue from '@vitejs/plugin-vue'
|
|||
import svgLoader from "vite-svg-loader";
|
||||
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
import { viteSingleFile } from 'vite-plugin-singlefile'
|
||||
|
||||
import vuetify from 'vite-plugin-vuetify'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({mode}: ConfigEnv) => {
|
||||
|
@ -25,6 +25,7 @@ export default ({mode}: ConfigEnv) => {
|
|||
svgLoader(),
|
||||
cssInjectedByJsPlugin(),
|
||||
viteSingleFile(),
|
||||
vuetify(),
|
||||
],
|
||||
define: {},
|
||||
resolve: {
|
||||
|
|
Loading…
Reference in New Issue