Покрокова фулстек-розробка на прикладі системи діагностування

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Усім привіт. Мене звати Олександр Шпуряка і я інженер-програміст в компанії ТОВ «НВП „Преобразователь-комплекс“».

У цій статті я поділюсь своїм досвідом та баченням на прикладі розробки проєкту діагностики та налаштувань системи управління тиристорного пристрою (проєкт на GitHub); як з наявних програмних інструментів у вільному доступі можна зібрати програмний комплекс; для чого інтегрувати в єдину систему різноманітні технології, фреймворки та інше.

Основні мови написання Java і JavaScript. Більш детальний огляд проєкту та його складових дивіться на сайті.

Мета проєкту полягає в створенні інтерактивної інтелектуальної інформаційної системи (IIC), яка є представленням програмного комплексу, лінгвістичних і логіко-математичних засобів для реалізації візуалізації системи управління, інтерпретації даних для їхньої обробки, діагностування, моніторингу й інтелектуальної пошукової системи.

Приклад розробки програми є корисним як для програмістів, так й інженерів.

Складові компоненти проєкту

Програмний комплекс складається з Front-end частини на стороні клієнта (web), що стосується SVG, CSS, HTML і JavaScript та Back-end частини у вигляді серверу, написаного на мові Java та складеного з таких фреймворків: JavaFX, SpringBoot, JDBC, MySql, Thymeleaf, Deeplearning4j, з підтримкою протоколів WebSocket, STOMP та об’єктів JSON.

Малюємо графіку SVG та оживлюємо її

Використання SVG — це доволі цікаве рішення для інтерактивної схеми. За допомогою JavaScript ми змінюємо властивості атрибутів складових схеми, чим досягаємо інтерактивності та певного функціоналу. Наприклад, нижче описання елементу тексту на схемі та функція для зміни вмісту самого тексту по атрибуту id="status".

<text x="650" y="390" font-family="Verdana" font-size="15" fill="blue" id="status">Статус:Ні</text>
function connect() {
   stompClient.activate();
   document.getElementById("status").textContent = "Статус:Так";
}

Також у цьому форматі реалізуємо анімацію для заставки splash screen для desktop-застосунку на сервері. Нижче скрин з лінком для представлення ефекту анімації.

У цьому випадку елемент <path>, за допомогою якого створюють лінії, криві, фігури, має також атрибут id, наприклад, елемент з id="Hand". За подією «клік» та графіком SVG запускається функція myAnime в CSS, яка реалізує ефект анімації.

// SVG
<path  d="m 170.862,73.211 c -4.142,……… ,-18.134" id="Hand" />

// JavaScript
var myAnimeClick = document.getElementById('Capa_1');
   myAnimeClick.addEventListener('click', function(){
   this.classList.toggle('addAnimation');
});

// CSS
#Capa_1.addAnimation #Hand {
    animation: myAnime 1.5s ease-out;
}
@keyframes myAnime {
<!-- Переміщення руки до гори -->
0% {   <!-- Початкове положення --> 
   transform-origin: 0% 0%;   
   transform: translateY( 0px )
} 
50% {  <!-- В процесі --> 
   transform-origin: 0% 0%;   
   transform: translateY( -80px )
}	
100% { <!-- Кінцеве положення --> 
   transform-origin: 0% 0%;   
   transform: translateY( 0px )
}

Помічник для розробки вебзастосунку

Для допомоги в побудові Front-end частини, а саме виводу БД, використовуємо фреймворк Thymeleaf, який є серверним движком шаблонів Java як для веб-, так і для автономних середовищ. Це така собі більш сучасна версія JSP.

Для цього на стороні сервера просто додаємо залежність <artifactId>spring-boot-starter-thymeleaf</artifactId>. Для роботи з БД залежність <artifactId>spring-boot-starter-jdbc</artifactId>, з якої візьмемо центральний клас JdbcTemplate, який виконує основний робочий процес JDBC, залишаючи програмний код для надання SQL і вилучення результатів.

По-перше, створимо сервіс читання даних з БД:

@Service
public class DBService {
   @Autowired
   private JdbcTemplate jdbcTemplate;
   public List<ValuelistModel> getData(){
      String selectSql = "SELECT * FROM ValueList";
      List<ValuelistModel> insertedRows = jdbcTemplate.query(selectSql,
(resultSet, rowNum) -> new ValuelistModel( resultSet.getInt( "numberElement" ), resultSet.getFloat( "valueElement" ), resultSet.getString( "nameElement" ), resultSet.getString( "numberSvgElement" )));
      return insertedRows;
   }
}

По-друге, передамо дані з Thymeleaf на контролер:

@Controller
public class IndexController {
   @Autowired
   DBService dbservice;
   @RequestMapping("/")
   public String index(Model model) {
      model.addAttribute("valueList", dbservice.getData());
      //Print read records:
      dbservice.getData().forEach(System.out::println);
      return "index";
   }
}

І останнє: дані передаються в index.html в таблицю через префікс th:text="${ValueList.id_element }", який є одним з атрибутів стандарту Thymeleaf.

<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
     <link rel="stylesheet" th:href="@{/css/table.css}" />
  </head>
  <body class="align">
    <table id="example" class="table table-bordered" style="width: 100%">
	<thead>
	    <tr>
                <th>numberElement</th>
                ...
            </tr>
       </thead>
       <tbody>
           <tr th:each="ValueList: ${valueList}">
		<td th:text="${ValueList.id_element}"></td>
                ...
           </tr>
       </tbody>
     </table>
   </body>p;gt;
   <a href="../function.svg"><img class="alignleft size-full wp-image-2811" title="function" src="../function.svg" alt="" />
</html>

Самі файли розташовані на стороні серверу. Нижче скрин побудови вебзастосунку з таблицею даних та інтерфейсом.

Зв’язок та передача даних

Зв’язок клієнтського вебзастосунку з сервером виконується за допомогою протоколу STOMP на основі WebSocket. STOMP визначає формат і правила обміну даними, є текстово-орієнтованим протоколом та може мати як текстову, так і двійкову форму.

WebSocket, своєю чергою, це протокол, який забезпечує повнодуплексні канали зв’язку через одне з’єднання TCP. Це єдине з’єднання потім використовується для всіх майбутніх комунікацій. У ролі обміну даних використовуються об’єкти JSON. Додаємо до проєкту такі залежності:

<div class="hl-wrap html"><pre><artifactId>spring-boot-starter-websocket</artifactId>
<artifactId>gson</artifactId></pre></div>

До JavaScript, у файл SVG, необхідно додати SockJS, яка є бібліотекою JavaScript-браузера та надає об’єкт, подібний до WebSocket. SockJS використовується для ввімкнення резервних варіантів для браузерів, які не підтримують WebSocket. Spring надає реалізацію SockJS на стороні сервера.

Для сервера необхідно налаштувати контролер, а саме: кінцеві точки, тобто прийом та відправлення посилки:

@Controller
public class AnswerController {
  @MessageMapping("/answer") // Прийом повідомлень на сервер від клієнта.
  @SendTo("/topic/answers")       // Відправка відповіді клієнту.
  public Answer answer(ValuesMessage message) throws Exception {
    Thread.sleep(1000);
    final String time = new SimpleDateFormat("HH:mm").format(new Date());
    return new Answer(message.getName(), message.getText(), time);
  }

Для клієнта також прописуються кінцеві точки на JavaScript:

const stompClient = new StompJs.Client({
        brokerURL:   'ws://' +host  + '/chat-websocket'
});
// Прийом повідомлення.
stompClient.onConnect = (frame) => {
   console.log('Connected: ' + frame);
   stompClient.subscribe('/topic/answers', (answer) => { 
      showAnswer(JSON.parse(answer.body));        
    });
}
// Відправка повідомлення.
function connectData() {
   stompClient.publish({
      destination: "/app/answer",   
      body: JSON.stringify({"name":data,
                              'text':text           });
}

Нижче скрин з відображенням консолі в браузері з посилкою та отриманням об’єктів JSON:

Back-end. Простий запуск сервера

Для реалізації сервера використовуємо фреймворк SpringBoot, який містить такі інструменти, як-от Autoconfiguration для автоконфігурації наявних технологій у самому фреймворку, StarterDependencies для підтягування необхідних залежностей (бібліотек) та EmbeddedTomcat інтегрованого сервлет-контейнеру або вебсерверу, на якому працює вебзастосунок (інтерфейс на стороні клієнта).

Для створення початкового проєкту достатньо скористатися або інтегрованим інструментом SpringTool в IDE Eclipse або сайтом start.spring.io, де налаштовуються мова, проєкт, версія та бібліотеки. Основна залежність — це Spring Reactive Web з основним набором інструментів для запуску серверу.

Для потреб редагування інтерфейсу та іншого функціоналу прописуємо графічний застосунок на стороні сервера. Для цього поєднаємо JavaFX та SpringBoot, додавши до проєкту в файл pom.xml базові залежності для підтримки графіки, як-от підтримка керування інтерфейсу, API FXML, API-сцени та базові API для набору інструментів JavaFX UI.

<div class="hl-wrap html"><pre><artifactId>javafx-controls</artifactId
<artifactId>javafx-fxml</artifactId> 
<artifactId>javafx-graphics</artifactId
<artifactId>javafx-base</artifactId></pre></div>  

Для коректного запуску обох застосунків, Spring and JavaFX, треба замінити SpringApplication.run на Application.launch, якій є основним класом додатку JavaFX, якій буде відповідати за запуск контекту Spring.

@SpringBootApplication
Public class BootFXApplication{
   Public static void main(String[]args){
     /*Заміна SpringApplication.run(JavafxApplication.class, args);*/
     Application.launch(JavafxApplication.class,args);
   }
}

Нижче на скрині справа графічний застосунок на сервері, та зліва вебзастосунок на стороні клієнта. Використовують один файл SVG, що дає можливість редагувати як настільний застосунок, так одночасно і веб.

Інтегрування ШІ для обробки запиту між Front-end та Back-end

Для реалізації інтелектуальної частини проєкту впровадимо лінгвістичні і логіко-математичні засоби, для чого інтегруємо фреймворк Deeplearning4j, який реалізує один з типів штучного інтелекту, а саме NLP (обробка природної мови).

Впровадимо в програму механізм семантичного пошуку, використовуючи базу даних в вигляді текстового документа. Що більше словник (БД), то більш релевантними будуть відповіді системи на запити клієнта. Основою для цього алгоритму є модель, яка навчається за даним словника.

У загальному вигляді для навчання моделі відбувається ітерація і токенізація, де текст розбивається за рядками, які розбиваються послівно, і за рахунок регулярного виразу відкидаються знаки пунктуації.

У кінці відбувається саме навчання, що передбачає налаштування полів метода ParagraphVectors. Зрештою ми отримуємо всі слова у вигляді векторів. Вхідне речення для пошуку також перетворюється в цифри і порівнюється з кожною сумою векторів рядка, що є в моделі.

Нижче алгоритм навчання і функція порівняння:

//Налаштування навчання
paragraphVectors = new ParagraphVectors.Builder()
  .minWordFrequency(1)    //Частота повторюємих слів.
  .iterations(5).epochs(1).layerSize(100).learningRate(0.025) 
  .labelsSource(source)      // Джерело міток.
  .windowSize(5)                 // Розмір контекстного вікна.
  .iterate(sentenceIterator)  // Навчальний корпус.
  .trainWordVectors(false)
  .vocabCache(cache)      
  .tokenizerFactory(tokenizerFactory).sampling(0)         
  .build();
paragraphVectors.fit();        // Навчання.
// Функція навчання
public static double cosineSimForSentence(Word2Vec vector, String sentence1, String sentence2){
   Collection<String> label1 = Splitter.on(' ').splitToList(sentence1);
   Collection<String> label2 = Splitter.on(' ').splitToList(sentence2);
   try{
      return Transforms.cosineSim(vector.getWordVectorsMean(label1),   vector.getWordVectorsMean(label2));
   }catch(Exception e){
      exceptionMessage = e.getMessage();
   }
   return Transforms.cosineSim(vector.getWordVectorsMean(label1), vector.getWordVectorsMean(label2));
}

Нижче скрин отримання відповіді клієнтом на його запит серверу.

Наприклад, запит від клієнта «N#=0» (це може бути речення). Система вибирає найбільш схожу відповідь за значенням косинуса подібності. У цьому випадку ми отримали відповідь у вигляді речення «0.34971187 n#=0 — або не задана швидкість, або n#-масштаб = 0». Де 0.34971187 — це найбільш висока схожість за пошуком у моделі.

Висновок

Розглянуті вище програмні інструменти доволі просто поєднуються і можуть стати прикладом написання простого вебзастосунку у вигляді інтерфейсу, який легко спроєктувати для опису технологічного або фізичного процесу, а також інтерактувати необхідні елементи на малюнку для взаємодії з користувачем.

👍ПодобаєтьсяСподобалось3
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі