commit 67677301817f5e8a5f1493579f32f65a78371a83 Author: Alexander Popov Date: Tue Aug 29 22:33:32 2023 +0300 first diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8b93d91 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,28 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.c,*.h}] +indent_style = space +indent_size = 4 + +[*.js] +indent_style = space +indent_size = 2 + +[{*.html,*.css}] +indent_style = tab +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6823c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Build & binary +brakeconf +dist +webui +gui/bootstrap* +gui/ubuntu-font.css +src/html.h diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d122067 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +package-lock.json +*.md diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..8994389 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "printWidth": 100, + "bracketSpacing": true, + "bracketSameLine": true, + "semi": true, + "singleQuote": true, + "arrowParens": "always", + "htmlWhitespaceSensitivity": "strict", + "endOfLine": "lf" +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..eea5739 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +* [Ubuntu](https://assets.ubuntu.com/v1/0cef8205-ubuntu-font-family-0.83.zip) font family. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..77c3c55 --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +clear + +rm dist/* &> /dev/null +rm brakeconf &> /dev/null +python3 ./build_gui.py gui/index.html dist/index.html &> /dev/null +xxd -i -n html_document dist/index.html > src/html.h +# sed -i 's/unsigned char/const unsigned char/g' src/html.h +gcc -I./webui -o brakeconf src/device.c src/main.c src/ui.c webui/libwebui-2-static.a -lserialport -ljansson + +./brakeconf icanthink diff --git a/build_gui.py b/build_gui.py new file mode 100644 index 0000000..45d3d18 --- /dev/null +++ b/build_gui.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +import sys, re, os +from collections import deque +from bs4 import BeautifulSoup, Tag +from jsmin import jsmin +from csscompressor import compress + +# html param +html = sys.argv[1] +# target param +target = sys.argv[2] +# path from html param +path = re.sub(r"[^\/]*$", "", html) +# open html file +soup = BeautifulSoup(open(html), 'html.parser') +# find last script as anchorpoint +lastScript = soup.findAll("script", attrs = {"src" : True})[-1] +# get all scripts containing src attribute (= external scripts) +scripts = soup.findAll("script", attrs = {"src" : True}) +# find last style link as anchorpoint +lastStylesheet = soup.findAll("link", attrs = {"rel" : "stylesheet"})[-1] +# get all links to css stylesheets +stylesheets = soup.findAll("link", attrs = {"rel" : "stylesheet"}) + +# create list of script srcs +print("\nRead Scripts:") +scriptsSrc = deque() +for script in scripts: + scriptsSrc.append(path + script.attrs["src"]) + print("\t" + path + script.attrs["src"]) + +# create list of stylesheets srcs +print("\nRead Stylesheets:") +stylesheetsSrc = deque() +for stylesheet in stylesheets: + stylesheetsSrc.append(path + stylesheet.attrs["href"]) + print("\t" + path + stylesheet.attrs["href"]) + +# merge scripts to temp.js +print("\nMerge Scripts:") +print("\t", end="") +with open("temp.js", "w") as outfileScript: + for fname in scriptsSrc: + # add space every script + if fname != 'gui//webui.js': + outfileScript.write("\n") + print("~", end="") + with open(fname) as infile: + for line in infile: + outfileScript.write(line) +print("\n"); + +# merge stylsheets to temp.css +print("Merge Stylesheets:") +print("\t", end="") +with open("temp.css", "w") as outfileCSS: + for fname in stylesheetsSrc: + # add space every script + outfileCSS.write("\n") + print("~", end="") + with open(fname) as infile: + for line in infile: + outfileCSS.write(line) +print("\n"); + +# minify javascript +print("Minify temp.js\n\t~") +with open("temp.js") as js: + minified_js = jsmin(js.read()) + +# minify css +print("\nMinify temp.css\n\t~") +with open("temp.css") as css: + minified_css = compress(css.read()) + +# replace scripts with merged and min embed script / css +print("\nReplacing and deleting\n\t~") +tag = soup.new_tag("script") +tag["type"] = "text/javascript" +tag.append(minified_js) +lastScript.replace_with(tag) + +tag = soup.new_tag("style") +tag["type"] = "text/css" +tag.append(minified_css) +lastStylesheet.replace_with(tag) + +#remove script and style tags +for script in scripts: + script.decompose() +for stylesheet in stylesheets: + stylesheet.decompose() + +#remove temp +os.remove("temp.js") +os.remove("temp.css") + +#save html as target +file = open(target,"w") +file.write(soup.prettify()) +file.close() + +print("\nFIN\n") diff --git a/gui/app.js b/gui/app.js new file mode 100644 index 0000000..72ea3b7 --- /dev/null +++ b/gui/app.js @@ -0,0 +1,177 @@ +'use strict'; + +const ENABLE_AUTO_CHECK_THEME = false; + +// Перечень кнопок переключения окон +const enumWindowButtons = [ + 'btnWindowDevice', + 'btnWindowTesting', + 'btnWindowSettings', + 'btnWindowFirmwareUpdate', + 'btnWindowLog', + 'btnWindowAbout', +]; + +// Перечень окон программы +const enumWindow = [ + 'appWindowDevice', + 'appWindowTesting', + 'appWindowSettings', + 'appWindowFirmwareUpdate', + 'appWindowLog', + 'appWindowAbout', +]; + +/* main */ +window.onload = function () { + /* Автоматически активирует тему в зависимости от настроек системы */ + if (ENABLE_AUTO_CHECK_THEME) { + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + switch_theme('dark'); + } else { + switch_theme('light'); + } + } + + document.getElementById('btn_theme_switch').addEventListener('click', switch_theme, false); + enumWindowButtons.forEach((btn) => { + document.getElementById(btn).addEventListener('click', ui_window_btn_active, false); + }); + + document.getElementById('btn_app_close').addEventListener('click', close_app); + document.getElementById('btn_port_refresh').addEventListener('click', refresh_ports); + document.getElementById('btn_device_connect').addEventListener('click', connect_to_device); +}; + +/** + * Изменяет тему приложения + */ +function switch_theme() { + let app = document.documentElement; + let theme = app.getAttribute('data-bs-theme'); + + if (theme == 'auto' || theme == 'light') { + app.setAttribute('data-bs-theme', 'dark'); + } else { + app.setAttribute('data-bs-theme', 'light'); + } +} + +/** + * ... + */ +function ui_window_btn_active(clicked_btn) { + enumWindowButtons.forEach((btn) => { + const button = document.getElementById(btn); + button.classList.remove('active'); + }); + + clicked_btn.target.classList.add('active'); + + ui_show_window(clicked_btn.target.getAttribute('id').replace('btn', 'app')); +} + +/** + * ... + */ +function ui_show_window(window_name) { + const current_window = document.getElementById(window_name); + + enumWindow.forEach((w) => { + let each_window = document.getElementById(w); + each_window.classList.remove('d-flex'); + each_window.classList.add('d-none'); + }); + + current_window.classList.remove('d-none'); +} + +/** + * Обновляет список портов + */ +function refresh_ports() { + const port_selector = document.getElementById('port_selector'); + + webui.call('webui_refresh_ports').then((response) => { + // const ports_array = JSON.parse(response); + const ports_array = response; + + // Проверка длины массива + if (ports_array.length > 0) { + port_selector.innerHTML = ''; + } else { + alert('Отсутсвуют подключённые устройства!'); + return 1; + } + + ports_array.ports.forEach((port, index) => { + if (index == 0) { + const default_option = document.createElement('option'); + default_option.innerText = 'Выберите порт...'; + default_option.value = 'null'; + default_option.selected = true; + + port_selector.appendChild(default_option); + } + + const option = document.createElement('option'); + option.innerText = port; + option.value = port; + + port_selector.appendChild(option); + }); + }); +} + +/** + * Подключается к устройству + */ +function connect_to_device() { + const port_selector = document.getElementById('port_selector'); + const btn_port_refresh = document.getElementById('btn_port_refresh'); + const btn_device_connect = document.getElementById('btn_device_connect'); + + if (port_selector.value == 'null') { + alert('Необходимо выбрать порт!'); + return; + } + + webui.call('webui_connect_device', port_selector.value); + + if (btn_device_connect.innerText == 'Подключиться') { + port_selector.disabled = true; + btn_port_refresh.disabled = true; + + btn_device_connect.classList.add('btn-success'); + btn_device_connect.classList.remove('btn-primary'); + btn_device_connect.innerText = 'Отключиться'; + } else { + port_selector.disabled = false; + btn_port_refresh.disabled = false; + + btn_device_connect.classList.add('btn-primary'); + btn_device_connect.classList.remove('btn-success'); + btn_device_connect.innerText = 'Подключиться'; + } +} + +/** + * Добавляет строку в окно лога + */ +function add_line_log(text) { + const connection_log_console = document.getElementById('connection_log_console'); + connection_log_console.innerHTML += `${text}\n`; + + /* Автоматически прокручивает лог в конец */ + const sw_connection_log_autoscroll = document.getElementById('sw_connection_log_autoscroll'); + if (sw_connection_log_autoscroll.checked) { + connection_log_console.scrollTop = connection_log_console.scrollHeight; + } +} + +/** + * + */ +function close_app() { + webui.call('close_app', 'a').then((response) => {}); +} diff --git a/gui/index.html b/gui/index.html new file mode 100644 index 0000000..f74fd09 --- /dev/null +++ b/gui/index.html @@ -0,0 +1,319 @@ + + + + + + Настройка модуля торможения + + + + + + + +
+ +
+ +
+ +
+ + +
+
+ +
+
+
+ + +
+ +
+ + + + +
+ + + +
+ + + + + + + +
+ + + +
+ Команда + + +
+
+ + +
+ settings +
+ + +
+ FW update +
+ + +
+
+ +
+ + +
+
+
+ + +
+
+ Программа для настройки + модуля торможения. +
+
+ Разработано специально для + НК-Сервис. +
+
2023 by Alexander Popov
+
+
+
+ + diff --git a/gui/styles.css b/gui/styles.css new file mode 100644 index 0000000..cdd0cf2 --- /dev/null +++ b/gui/styles.css @@ -0,0 +1,17 @@ +body { + font-family: 'Ubuntu'; +} + +.container { + max-width: 540px; +} + +textarea { + /* color: var(--bs-gray-400) !important;*/ + font-family: 'Ubuntu Mono'; + font-size: 0.8rem !important; +} + +.link-pointer { + cursor: pointer; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2cccf8f --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "private": true, + "name": "...", + "version": "1.0.0", + "description": "...", + "author": "Alexander Popov ", + "scripts": { + "prettier": "npx prettier --write gui", + "clean": "rm -rf ./dist/* &> /dev/null" + }, + "devDependencies": { + "prettier": "3.0.1" + } +} diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..f53ed95 --- /dev/null +++ b/src/device.c @@ -0,0 +1,34 @@ +/* + * AUTHOR: Alexander Popov + * DESC: ... + */ + +#include "device.h" + +json_t *get_serial_ports() { + struct sp_port **port_list; + enum sp_return result = sp_list_ports(&port_list); + + json_t *ports_data = NULL; + json_t *ports_array = NULL; + + ports_array = json_array(); + + if (result == SP_OK) { + /* Get the name of the port. */ + int i; + for (i = 0; port_list[i] != NULL; i++) { + struct sp_port *port = port_list[i]; + char *port_name = sp_get_port_name(port); + + json_array_append(ports_array, json_string(port_name)); + } + + sp_free_port_list(port_list); + } + + ports_data = json_object(); + json_object_set_new(ports_data, "ports", ports_array); + + return ports_data; +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..bebd9fc --- /dev/null +++ b/src/device.h @@ -0,0 +1,14 @@ +/* + * AUTHOR: Alexander Popov + * DESC: ... + */ + +#ifndef DEVICE_H_ +#define DEVICE_H_ + +#include +#include + +json_t *get_serial_ports(); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9607026 --- /dev/null +++ b/src/main.c @@ -0,0 +1,30 @@ +#include +#include + +#include "webui.h" +#include "html.h" +#include "ui.h" +#include "device.h" + +struct sp_port *serial_port; + +int main(int argc, char const *argv[]) { + int app_window = webui_new_window(); + + html_document[html_document_len] = '\0'; + + if (argc > 1 && strcmp(argv[1], "icanthink") == 0) { + } + else { + webui_set_kiosk(app_window, true); + } + + webui_bind(app_window, "close_app", close_app); + webui_bind(app_window, "webui_refresh_ports", refresh_devices); + webui_bind(app_window, "webui_connect_device", connect_device); + + webui_show(app_window, html_document); + webui_wait(); + + return 0; +} diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..0627e00 --- /dev/null +++ b/src/ui.c @@ -0,0 +1,24 @@ +/* + * AUTHOR: Alexander Popov + * DESC: ... + */ + +#include "ui.h" + +void close_app(webui_event_t* e) { + printf("Bye!\n"); + // webui_destroy(); + webui_exit(); +} + +void refresh_devices(webui_event_t* e) { + printf("ololo\n"); + + webui_return_string(e, json_dumps(get_serial_ports(), 0)); +} + +void connect_device(webui_event_t* e) { + const char* str = webui_get_string(e); + + webui_return_bool(e, true); +} diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..3832895 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,20 @@ +/* + * AUTHOR: Alexander Popov + * DESC: ... + */ + +#ifndef UI_H_ +#define UI_H_ + +#include +#include +#include +#include "webui.h" + +#include "device.h" + +void close_app(webui_event_t* e); +void connect_device(webui_event_t* e); +void refresh_devices(webui_event_t* e); + +#endif