27 МОСКОВСКАЯ ВСТРЕЧА
Форум самогонщиков Сайт Барахолка Магазин 27 МОСКОВСКАЯ ВСТРЕЧА

Smart Distiller (Умный дистиллятор с управлением по интернет)

Форум самогонщиков Автоматика
1 ... 6 7 8 9 10 11 12 13 9
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.160  06 Сент. 17, 06:38
Базы данных сквозь призму flask-sqlalchemy часть 1
     Продолжаем наше погружение во Flask.

    При использовании прямого вызова метода DS18B20.Measure в предыдущей версии нашего приложения наблюдается задержка обновления температурных данных на веб-странице. Это связано с тем, что цикл температурного преобразования цифровых термометров занимает порядка одной секунды, и построителю веб-страницы (flask.render_template()) приходится ждать завершение этого преобразования, прежде чем представить новую веб-страницу с новыми температурными данными.
    Решение – в разделении задач по потокам. При инициализации приложения запускается модуль в отдельном потоке, который в цикле стартует процедуру измерения цифровых термометров DS18B20, ожидает её завершение и сохраняет свежие температурные данные в какое-то хранилище. Построитель же веб-страницы будет получать эти температурные данные уже из хранилища.
    Конечно, можно было бы хранить список термометров в какой-либо переменной в памяти. Но есть один довод в пользу постоянного хранения параметров термометров в базе данных. Автоматика должна знать, в каком месте установлен конкретный термометр, чтобы производить соответствующие управляющие действия. Информация о местоположении термометра не должна исчезать после отключения питания, поэтому её нужно хранить в базе данных вместе с другими параметрами термометра.
    Выше уже описывалась соответствующая модель базы данных ([сообщение #12969061]).
    Для работы с базой данных нам потребуются:
1. Добавить в виртуальное окружение python пакет flask-sqlalchemy;
2. Создать при инициализации приложения и сконфигурировать объект sqlalchemy для работы с базами данных и связать этот объект с приложением flask;
3. Описать модели таблиц баз данных;
4. Написать класс-поток, сохраняющий актуальные температурные значения в базу данных;
5. Научить построитель веб-страницы получать температурные значения из базы данных.

Добавление пакета flask-sqlalchemy
    В обозревателе решений разворачиваем Окружения Python, жмем правой кнопкой мыши на папке env, из контекстного меню выбираем Установить пакет Python... В строчке, где написано Поиск PyPI и установленных пакетов, набираем flask-SQLAlchemy и выбираем пункт «установить flask-SQLAlchemy с помощью pip» из PyPI. В нижнем окне Вывод можно наблюдать ход установки. По завершении установки пакета, открыв вкладку Обозреватель решений можем увидеть в списке виртуального окружения Flask-SQLAlchemy(2.2).

Объект sqlalchemy для работы с базами данных
    Объект sqlalchemy лучше создать в отдельном модуле с именем database.py в корне проекта, чтобы остальные модули проекта могли получать к нему доступ. Файл модуля содержит всего две строчки:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()


--------------------------------------
Colored with http://dumpz.org

    Конфигурирование объекта sqlalchemy на самом деле производится из конфигурации связываемого с ним объекта приложения flask.
    Например, для указания в конфигурации пути доступа к базе данных, можно было бы в процессе инициализации приложения flask добавить такие строки:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
    Если указывать конфигурационные параметры в коде, потом трудно будет ими управлять. Поэтому гораздо удобнее собрать все конфигурационные параметры в один файл с именем config.py:
import os
from datetime import datetime

class Config(object):
   """Содержит базовые параметры конфигурации приложения"""

   #Наименование управляемого устройства
   DEVICE_NAME = u'Дистиллятор'
   
   # Путь к каталогу запуска программы
   BASEDIR = os.path.abspath(os.path.dirname(__file__))

   # Определяет, включен ли режим отладки.
   # Если включен, flask будет показывать
   # подробную отладочную информацию. Если выключен -
   # - 500 ошибку без какой-либо дополнительной информации.
   DEBUG = False

   # Включение защиты против "Cross-site Request Forgery (CSRF)"
   CSRF_ENABLED = True

   # Случайный ключ, который будет использоваться для подписи
   # данных, например cookies.
   SECRET_KEY = "YOUR_RANDOM_SECRET_KEY"

   # URI используемый для подключения к базе данных
   # Основная база данных
   SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(BASEDIR, "Distiller.db")
   #Базы данных приложения
   SQLALCHEMY_BINDS = {'Distiller':'sqlite:///' + os.path.join(BASEDIR, "Distiller.db"),
                       'log':'sqlite:///' + os.path.join(BASEDIR, datetime.now().strftime('%Y_%m_%d %H-%M-%S.log'))}
   SQLALCHEMY_TRACK_MODIFICATIONS = False


--------------------------------------
Colored with http://dumpz.org


    В файле конфигурации написаны пути к базам данных SQLite. Основные параметры устройства будут храниться в базе данных в файле с именем Distiller.db. В базах данных с динамически формируемым по дате и времени именем файла будут записываться журналы работы устройства.

    Конфигурирование объекта приложения flask с объектом sqlalchemy сделаем в файле инициализации всего приложения Distiller.__init__.py:
"""
The flask application package.
"""


from flask import Flask

import Distiller.models
from Distiller.config import Config
from Distiller.database import db

app = Flask(__name__)
# Загрузка конфигурации из config.py
app.config.from_object(Config)

#Подключение, инициализация и создание (при необходимости) базы данных
db.app=app
db.init_app(app)
# Создать базу данных
db.create_all(bind='Distiller')

#Подключение функций представления веб-страниц
import Distiller.views


--------------------------------------
Colored with http://dumpz.org


Модели таблиц баз данных
    Модели таблиц баз данных хранятся в виде классов в модуле models.py в корневой папке программы. Создаем в папке Distiller пустой файл Python с именем models.py и наполняем его следующим содержимым:
from Distiller. database import db

class DS18B20(db.Model):
   '''Table of digital thermometers DS18B20'''
   __bind_key__ = 'Distiller'
   __tablename__ = 'DS18B20'

   id = db.Column(db.Integer, primary_key=True)
   IDthermometer = db.Column(db.String(16), nullable=False, unique=True)
   Name = db.Column(db.String(64), nullable=False, unique=True)
   T = db.Column(db.Float)
   Timestamp = db.Column(db.DateTime)

   def __str__(self):
       return self.Name


--------------------------------------
Colored with http://dumpz.org


Создание потока, сохраняющего температурные значения в базе данных и вывод этих значений из базы данных на веб-страницу - во второй части.
Текущее состояние проекта - в файле Distiller.zip
2017-09-05_10-59-34.png
2017-09-05_10-59-34.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-09-05_11-09-05.png
2017-09-05_11-09-05.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-09-05_11-14-37.png
2017-09-05_11-14-37.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 357.4 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.161  06 Сент. 17, 09:38
Базы данных сквозь призму flask-sqlalchemy часть 2
    Ещё пару шагов и можно будет утверждать, что мы асы flask-sqlalchemy.

    В предыдущей части проделана довольно большая подготовительная работа. Если запустить проект, который приложен в конце предыдущей части, то в корневой папке обнаружим, что появился файл Distiller.db, в котором хранится база данных проекта. Этот файл создается строчкой кода db.create_all(bind='Distiller') в Distiller. __init__.py.

    Модуль (DS18B20toDB.py), содержащий класс-поток для сохранения температурных значений в базе данных расположим в папке для вспомогательных программ с именем helpers (создаем её в папке Distiller). В этой папке также создаем пустой файл с именем  __init__.py. Содержимое модуля DS18B20toDB.py:
"""
$Id: DS18B20toDB.py,v 1.0 2017/07/19

Класс-поток обеспечивает запись значений температур от
цифровых термометров в базу данных.
Данные будут записаны, как только будут получены.
"""


import threading, time
from Distiller import models, app, dbLock
           
from Distiller.sensors.DS18B20 import Measure



class DS18B20toDB(threading.Thread):
    u"""Класс записывает показания DS18B20 в базу данных"""

    # событие записи в БД очередных показаний термометров
    ReadyDS18B20=threading.Event()
    # список термометров с их показаниями
    TlistToClient=[]

    def __init__(self):
        u'''инициализация'''
        #threading.Thread.__init__(self)
        super(DS18B20toDB, self).__init__()
        self.__Run=False

    def run(self):
        self.__Run=True
        while self.__Run:
            # Если флаг готовности температурных данных в БД установлен, сбросить его
            if self.ReadyDS18B20.isSet(): self.ReadyDS18B20.clear()
            # Получаем температурные данные(параметром передаётся таймаут в секундах)
            Tlist=Measure(6)
            # Блокируем другие потоки
            dbLock.acquire()
            # Ложим в базу температурные данные
            for eashT in Tlist:
                Thrmmtr=models.DS18B20.query.filter_by(IDthermometer=eashT[0]).first()
                if Thrmmtr==None:
                    #Если в БД не найден термометр с соответствующим ID, сохраняем новый термометр
                    Thrmmtr=models.DS18B20(IDthermometer=eashT[0], Name=u'', T=eashT[1], Timestamp=eashT[2])
                else:
                    #Если найден, изменяем температуру и время её фиксации
                    Thrmmtr.T=eashT[1]
                    Thrmmtr.Timestamp=eashT[2]
                models.db.session.add(Thrmmtr)
            models.db.session.commit()  #сохраняем изменения в БД
            # Разблокируем другие потоки
            dbLock.release()
            self.GiveTemperatureFromDB()
            # Вскидываем флаг готовности свежих температурных данных в БД
            self.ReadyDS18B20.set()
        return

    def stop(self):
        self.__Run=False

    def GiveTemperatureFromDB(self):
        u'''Собирает и сохраняет список кортежей имен термометров
        и значений температур в TlistToClient'''

        dbLock.acquire()
        self.TlistToClient=[]
        for thermometer in models.DS18B20.query.all():
            self.TlistToClient.append((thermometer.IDthermometer, thermometer.T))
        dbLock.release()


--------------------------------------
Colored with http://dumpz.org

    Созданный из этого класса и запущенный объект в своем потоке получает температурные данные, сохраняет их в БД и вскидывает флаг готовности свежих температурных данных. Помимо этого, сохраняет температурные данные из БД в своей переменной TlistToClient, которая доступна другим модулям.

    Создание и запуск объекта-потока осуществляется в корневом модуле __init__.py:
"""
The flask application package.
"""


import threading
from flask import Flask

import Distiller.models
from Distiller.config import Config
from Distiller.database import db

# Объект для блокировки потоков при доступе к совместным ресурсам
dbLock = threading.Lock()

# Объект flask
app = Flask(__name__)
# Загрузка конфигурации из config.py
app.config.from_object(Config)

# Подключение, инициализация и создание (при необходимости) базы данных
db.app=app
db.init_app(app)
# Создать базу данных
db.create_all(bind='Distiller')

# Запуск потока, записывающего температурные значения в БД
from Distiller.helpers.DS18B20toDB import DS18B20toDB
ds18B20toDB=DS18B20toDB()
ds18B20toDB.name='ds18B20toDB'
ds18B20toDB.start()


# Подключение функций представления веб-страниц
import Distiller.views


--------------------------------------
Colored with http://dumpz.org


    Для блокировки других потоков при совместном доступе к базе данных введен специальный объект dbLock.

    Теперь функция представления веб-страницы может получить температурные значения от объекта-потока DS18B20toDB:
"""
Routes and views for the flask application.
"""


from datetime import datetime
from flask import render_template
from Distiller import app, ds18B20toDB
from Distiller.sensors import DS18B20

@app.route('/')
def home():
    """Renders the home page with temperature data."""
    return render_template(
        'layout.html',
        Tlist=ds18B20toDB.TlistToClient,
        title='Home Page',
        year=datetime.now().year,
    )


--------------------------------------
Colored with http://dumpz.org


    Если теперь запустить проект, то можем наблюдать, что данные обновляются гораздо живее. Однако моргание страницы при обновлении выглядит неприятно и непрофессионально.

Новая версия проекта – в файле Distiller.zip
Куски программ, которые приводятся в постах, предназначены для ознакомления при просмотре и могут содержать ошибки.
Отлаженный код содержится в архиве Distiller.zip
Distiller.zip 359.4 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.162  06 Сент. 17, 19:02
Web-сокеты – событийное предоставление информации, серверная часть.
     Наивное использование обновления окна для получения новой информации конечно же нельзя использовать для веб-интерфейса.
    Помимо постоянного дёргания окна браузера при перезагрузке веб-страницы ещё и растёт трафик, потому что страница перезагружается целиком, а не та её часть, которая требует обновления.
    Эти проблемы можно вполне успешно решить использованием Ajax-jQuery запросов (http://flask.pocoo.org/docs/0.12/patterns/jquery/, https://pythonprogramming.net/jquery-flask-tutorial/).
    Однако не стоит забывать, что веб-интерфейс создается не только и не столько к веб-серверу, а скорее к устройству, у которого помимо основных функций имеется и функция веб-сервера. И на этом устройстве происходят различные события (поступили свежие температурные значения, сменился режим работы, изменилось значение мощности нагрева, открылся/закрылся один из клапанов и т.д.). Об этих событиях веб-сервер должен незамедлительно уведомлять всех подключенных к нему клиентов. Да-да, хочется, чтобы и коллеги-самогонщики могли наблюдать за работой аппаратуры через интернет.
    Если отбросить такие монументальные технологии как Adobe Flash и Silverlight, то самой приемлемой для направления уведомлений с серверной стороны остается даже не технология, а протокол полнодуплексной связи WebSocket. Этот протокол позволяет направлять блок данных как по инициативе сервера - клиенту, так и по инициативе клиента – серверу. Соответственно, должны быть программы-обработчики для приема данных как на клиентской, так и на серверной стороне.
    К счастью, протокол полнодуплексной связи WebSocket реализован в Socket.IO (https://socket.io/) — JavaScript-библиотеке для веб-приложений и обмена данными в реальном времени – для клиента и в Flask-SocketIO (http://flask-socketio.readthedocs.io/en/latest/) – пакете микрофреймворка Flask – для сервера.
    Идея следующая:
на сервере установить передатчик, который по сигналу готовности температурных значений от потока DS18B20toDB, записывающего эти значения в базу данных, передаст температурные значения клиенту;
на клиенте установить приемник температурных значений, который и отобразит их на веб-странице.

Установка пакета Flask-SocketIO в виртуальное окружение.
    В обозревателе решений разворачиваем Окружения Python, жмем правой кнопкой мыши на папке env, из контекстного меню выбираем Установить пакет Python... В строчке, где написано Поиск PyPI и установленных пакетов, набираем Flask-SocketIO и выбираем пункт «установить Flask-SocketIO с помощью pip» из PyPI.

Создание приложения flask_socketio из flask-приложения
    В корневом файле __init__.py импортируем пакет flask_socketio и создаем приложение socketio:
"""
The flask application package.
"""


import threading
from flask import Flask
from flask_socketio import SocketIO

import Distiller.models
from Distiller.config import Config
from Distiller.database import db

# Объект для блокировки потоков при доступе к совместным ресурсам
dbLock = threading.Lock()

# Объект flask
app = Flask(__name__)
# Загрузка конфигурации из config.py
app.config.from_object(Config)

#Создание приложения flask_socketio из flask-приложения
socketio = SocketIO(app)

# Подключение, инициализация и создание (при необходимости) базы данных
db.app=app
db.init_app(app)
# Создать базу данных
db.create_all(bind='Distiller')

# Запуск потока, записывающего температурные значения в БД
from Distiller.helpers.DS18B20toDB import DS18B20toDB
ds18B20toDB=DS18B20toDB()
ds18B20toDB.name='ds18B20toDB'
ds18B20toDB.start()


# Подключение функций представления веб-страниц
import Distiller.views


--------------------------------------
Colored with http://dumpz.org


Замена запуска приложения Flask на запуск приложения Flask-SocketIO в файле runserver.py:
"""
This script runs the Distiller application using a development server.
"""


from os import environ
from Distiller import app
from Distiller import socketio

if __name__ == '__main__':
   HOST = environ.get('SERVER_HOST', 'localhost')
   try:
       PORT = int(environ.get('SERVER_PORT', '5555'))
   except ValueError:
       PORT = 5555

   #app.run(HOST, PORT)
   #Запуск сервера с вебсокетами
   socketio.run(app, HOST, PORT, debug=True)


--------------------------------------
Colored with http://dumpz.org

Пока отправку температурных данных добавим к классу DS18B20toDB, добавив одну строчку в конце его метода GiveTemperatureFromDB:
 socketio.emit('DataFromServer', {'Thermometers' : self.TlistToClient})

Предварительно socketio нужно импортировать из пакета Distiller.

Текущее состояние проекта – в файле Distiller.zip
2017-09-06_23-06-12.png
2017-09-06_23-06-12.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 359.6 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.163  07 Сент. 17, 15:32
Web-сокеты – событийное предоставление информации, клиентская часть.

    Теперь делаем кувырок назад и превращаемся в программиста JavaScript, HTML и иже с ними.
    Осталось научить нашу веб-страничку layout.html принимать через веб-сокеты и отображать информацию от сервера.
    Убираем мета-тег, который мы использовали для обновления веб-страницы (<meta http-equiv="Refresh" content="1" />).
    Реализацию протокола WebSocket получаем подключением соответствующего скрипта в контейнере <head> веб-страницы:
<script src="https://cdnjs.cloudflare.com/....3/socket.io.js"></script>
(лучше скачать с сайта https://socket.io/ и включить в проект файл socket.io.js и ссылку дать на него).

    Объект WebSocket можно создать после загрузки и готовности веб-странички:
var socket = io();

    Обработчик поступающей через веб-сокет информации можно назначить методом on объекта socket:
socket.on('DataFromServer', (data) => {
   ShowDeviceData(data);
});
    Нужно обратить внимание, что название сокета 'DataFromServer' должно в точности совпадать с наименованием сокета в передатчике данных на серверной стороне. Соответствующий скрипт включен в конце секции <body>.
    Полученные данные data обрабатываются функцией ShowDeviceData(data).
    Температурные значения отображаются в блочном элементе <div> с идентификатором id="Thermometers".

    После удаления не используемых скриптов веб-страница layout.html должна выглядеть примерно так:
layout.html
<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>{{ title }} - Distiller</title>
   <link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
   <link href="../static/content/site.css" rel="stylesheet" />
   <script src="../static/scripts/socket.io.js"></script>
   <script src="/static/scripts/jquery-1.10.2.js"></script>

   <script type="text/javascript" charset="utf-8">
       // Функция отображения данных, поступивших от сервера, на html странице
       function ShowDeviceData(msg) {
           //Отображение значений температур
           if ('Thermometers' in msg) {
               ShowThermometers(msg.Thermometers);
           }
       }
       function ShowThermometers(TempData) {
           $('div#Thermometers').html('');
           for (T in TempData) {
               $('div#Thermometers').append('<p>' + TempData[T][0] + ' = ' + TempData[T][1] + '°C</p>');
           }
       }
   </script>
</head>

<body>
   <div class="navbar navbar-inverse navbar-fixed-top">
       <div class="container">
           <div class="navbar-header">
               <a href="/" class="navbar-brand">Дистиллятор</a>
           </div>
       </div>
   </div>

   <div class="container body-content">
       <div id="Thermometers">
       </div>
       <hr />
       <footer>
           <p>© {{ year }} - Distiller</p>
       </footer>
   </div>

   <script type="text/javascript" charset="utf-8">
       $(document).ready(
           function () {
               //создание объета веб-сокетов
               socket = io();

               // Веб-сокет, принимающий и отображающий данные с сервера
               socket.on('DataFromServer', (data) => {
                   //alert('Получены данные');
                   ShowDeviceData(data);
               });
           }
       );
   </script>
</body>
</html>


--------------------------------------
Colored with http://dumpz.org

    Запускаем и наслаждаемся проделанной работой. Страница не моргает, температурные значения отображаются сразу, как только были получены. Никто их не запрашивает, сервер сам шлёт их всем подключенным клиентам.

В файле Distiller.zip – текущая версия проекта.
2017-09-07_19-36-25.png
2017-09-07_19-36-25.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 284.8 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.164  07 Сент. 17, 18:14
Термометры – стрелочные приборы google.
     Займёмся более крутыми вещами – преобразуем унылый текстовый вывод температур в стрелочные показометры.
    Для этого используем замечательный пакет google chart (https://google-developers.appspot.com/...ve/docs/gallery).

    В секции <head> веб-страницы подключаем загрузчик Google API Visualization:
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

    Затем в скрипте ниже загружаем необходимый пакет:
google.charts.load('current', {'packages':['gauge']});

    После загрузки страницы устанавливаем программу, создающую объект для отрисовки стрелочных приборов:
google.charts.setOnLoadCallback(drawGauge.start);

    Для удобства работы с интерфейсом Google API Visualization создан объект drawGauge, включающий в том числе метод ShowThermometers, отображающий термометры. Соответственно ранее используемую функцию ShowThermometers() заменяем на этот метод.

    Теперь наша веб-страница имеет новый код:
layout.html
<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>{{ title }} - Distiller</title>
   <link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
   <link href="../static/content/site.css" rel="stylesheet" />
   <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
   <script src="../static/scripts/socket.io.js"></script>
   <script src="/static/scripts/jquery-1.10.2.js"></script>

   <script type="text/javascript" charset="utf-8">
       // Загрузка пакета gauge и запуск drawGauge.start по окончании загрузки
       google.charts.load('current', { 'packages': ['gauge'] });

       // Функция отображения данных, поступивших от сервера, на html странице
       function ShowDeviceData(msg) {
           //Отображение значений температур
           if ('Thermometers' in msg) {
               drawGauge.ShowThermometers(msg.Thermometers);
           }
       }
   </script>
</head>

<body>
   <div class="navbar navbar-inverse navbar-fixed-top">
       <div class="container">
           <div class="navbar-header">
               <a href="/" class="navbar-brand">Дистиллятор</a>
           </div>
       </div>
   </div>

   <div class="container body-content">
       <div id="Thermometers">
       </div>
       <hr />
       <footer>
           <p>© {{ year }} - Distiller</p>
       </footer>
   </div>

   <script type="text/javascript" charset="utf-8">
       $(document).ready(

           function () {
               try {
                   google.charts.setOnLoadCallback(drawGauge.start);
               }
               catch (ex) { alert(ex); }


               //создание объета веб-сокетов
               socket = io();

               // Веб-сокет, принимающий и отображающий данные с сервера
               socket.on('DataFromServer', (data) => {
                   //alert('Получены данные');
                   ShowDeviceData(data);
               });
           }
       );

       // Объект для отображения стрелочных приборов
       drawGauge = {
           // Настройка термометров
           //options определяет вид стрелочного термометра
           options: {
               greenFrom: 0, greenTo: 80,
               yellowFrom: 80, yellowTo: 90,
               redFrom: 90, redTo: 100,
               minorTicks: 10
           },
           // Функция отображения термометров
           ShowThermometers: function (Thermometers) {
               try { var TempData = new google.visualization.DataTable(); }
               catch (ex) { return; }
               TempData.addColumn('string', 'Thermometer');
               TempData.addColumn('number', 'Temperature');
               // показания стрелочных приборов
               TempData.addRows(Thermometers);
               //Объект chartT, привязанный к тегу div - отображает температурные данные с сервера (бакэнда)
               try { chartT.draw(TempData, this.options); }
               catch (ex) { }
           },

           // функция, создающая объект отображения приборов
           start: function () {
               //По готовности всей Google API Visualization создать объект отображения стрелочных приборов
               this['chartT'] = new google.visualization.Gauge(document.getElementById('Thermometers'));
           },
       };
   </script>
</body>
</html>


--------------------------------------
Colored with http://dumpz.org

    Запускаем и распираемся от гордости – какие мы крутые программеры.
2017-09-07_22-57-34.png
2017-09-07_22-57-34.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 317.6 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.165  11 Сент. 17, 10:00
Веб-дизайн.
И тут Остапа понесло...

    На самом деле название слишком громкое. В пределах одного поста немыслимо дать даже основы web-дизайна.
    Поэтому здесь будут общие соображения относительно того, как может выглядеть веб-дизайн самогонного аппарата.
    Всласть налюбовавшись чарующими движениями стрелок термометров, и придя к заключению, что индикатор мощности нагрева теперь можно реализовать в том же стиле, что и термометры, пытаешься представить, что ещё будет на странице.
    Вид стрелочных термометров толкает к мысли реализовать одностраничный веб-интерфейс в виде приборной панели со «светодиодным матричным табло» для отображения текстовой информации, «светодиодами-индикаторами» и подходящего вида кнопками.
    Подавляющее число рекомендаций по web-дизайну начинаются с «... откройте свой любимый графический редактор Adobe PhotoShop или CorelDraw...». Далее предлагается нарисовать вид будущей веб-странички и только потом приступить к её разработке.
    Увы, я не отношусь к числу следующих рекомендациям. Поэтому мой дизайн веб-страницы изменялся по ходу добавления в веб-интерфейс новых функций и получения новых знаний в области форматирования HTML. Это можно проследить на прикрепленных скриншотах.
    Хорошо, что Visual Studio позволяет сразу увидеть веб-страницу. Поэтому множество раз я вносил изменения, запускал проект на исполнение, смотрел, что получилось, снова изменял и снова смотрел. И так до тех пор, пока результат меня не устраивал.
    Последний вариант web-дизайна имеет: «светодиодное матричное табло» для отображения текстовой информации о текущем состоянии устройства (4 строки по 20 символов); панель стрелочных термометров; панель со стрелочным индикатором мощности нагрева; панель со светодиодными индикаторами состояния клапанов дефлегматора и конденсатора. Справа располагается блок кнопок, которые всегда присутствуют на веб-страницы. Состав кнопок в центре меняется в зависимости от режима работы.
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

    На серверной стороне добавлены: объект-поток регулировки мощности нагрева, объекты управления клапанами дефлегматора и конденсатора, передатчик данных от сервера, обработчик запросов от клиента на получение информации, приемник-обработчик события нажатия кнопки.
    В нынешнем виде проект представляет законченный веб-интерфейс, готовый к использованию и дальнейшему развитию.
    Можно, конечно, было бы прикрутить к нему уже существующую программу, реализующую алгоритм перегона. Только эта программа не умеет пользоваться веб-интерфейсом и сообщать об этапах и параметрах перегона, придётся её этому научить.

    И остаются неосвещёнными ещё пара вопросов: разграничение доступа к управлению системой и вывод проекта в продуктив на Raspberry PI.

    На этом этапе тему открываю для обсуждения и вопросов.
2017-06-20_17-25-30.png
2017-06-20_17-25-30.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-07-02-163446_1600x900_scrot.png
2017-07-02-163446_1600x900_scrot.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-07-17_20-39-54.png
2017-07-17_20-39-54.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-07-17_21-00-10.png
2017-07-17_21-00-10.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
2017-07-17_21-10-23.png
2017-07-17_21-10-23.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 804.7 Кб
HBB Кандидат наук Москва 357 94
Отв.166  11 Сент. 17, 13:32
Молодец, Володь! От всей души поздравляю!
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.167  11 Сент. 17, 14:57
c 9-го по 20-е октября буду в Москве - можем пожать друг-другу руки.
Inco Студент Донецк 47 10
Отв.168  11 Сент. 17, 15:22, через 26 мин
Очень симпатично!
Индикатор нагрева: может стоит показывать цифры не только процентов, но и величину мощности?
Самый простой метод - в настройках указываем номинальную мощность своего тэна, ну и простой пересчет.

И ещё, может не по теме, но вот нашел такую штучку для Raspberry Pi 3 (UART/I2C/аналоговый/цифровой интерфейс на борту микросхемы АЦП MCP3008  ):
https://ru.aliexpress.com/...0.47d739cZ9xI6X
Описание в pdf-ке

HBB Кандидат наук Москва 357 94
Отв.169  11 Сент. 17, 15:51, через 29 мин
c 9-го по 20-е октября буду в Москве - можем пожать друг-другу руки.C-Bell, 11 Сент. 17, 14:57

Дык можно и не только руки пожать, но и отметить это дело! 
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.


ЖДУ!
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.170  11 Сент. 17, 23:41
HBB
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.171  17 Сент. 17, 03:01
Автоопределение мест расположения термометров.
     Далее приступаем к реализации алгоритмов перегона.
    И первое, что необходимо сделать – определить где какой термометр расположен. Без знания мест расположения термометров автоматика не сможет принимать управленческие решения.
    Используемые для измерения температур DS18B20 подсоединены к однопроводной шине, следовательно, электрически определить где какой установлен не получится. Поэтому сделаем автоопределение мест установки термометров, исходя из соображения, что по пути следования пара из куба наиболее горячим будет термометр низа колонны, а наиболее холодным – термометр конденсатора. Нужно только создать условия, когда показания термометров выстроятся в температурный ряд.
    Алгоритм автоопределения мест установки термометров:
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

    Закипание определяется из условия: рост температуры на каком-либо термометре превысит 1°С.
    Новый модуль autolacation.py в пакете processes реализует алгоритм автоопределения. (Не забудьте залить в куб хотя бы воду перед запуском!)
    В модуль config.py добавлен список мест расположения термометров в порядке возрастания температур T_LOCATION.
    Для обеспечения процесса автоопределения мест расположения термометров модуль DS18B20toDB.py доработан. Теперь он умеет не только распознавать необходимость автоопределения, но и генерирует ошибку, если термометров на шине не столько, сколько указано в конфигурации.
    Соответственно, при запуске, если места установки термометров не определены, на дисплей выводится надпись: «Требуется определение мест расположения термометров» и становится доступной кнопка «Автоопределение». При нажатии на эту кнопку создается и запускается объект-поток, реализующий алгоритм автоматического определения мест установки термометров. Этот объект скрывает кнопку «Автоопределение» и делает доступной кнопку «Останов», нажатие которой отменяет процесс автоопределения.
    Ещё одно новшество – ведение журнала процесса в отдельной базе данных с именем «log». Эта база хранится в файле с именем <Дата Время>.log, создаваемом при каждом запуске устройства. Таким образом, можно вести архив журналов перегона.
    Модуль models.py доработан, включена база данных log, содержащая две таблицы: log и logT. Журнал показаний термометров удобнее вести в отдельной таблице logT. Между таблицами баз данных установлены связи.
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

    В базе данных появились две пока пустые таблицы для хранения параметров процессов перегона.
    Также были исправлены выявленные ошибки.

    После запуска просто нужно нажать кнопку «Автоопределение» и наблюдать за процессом. По завершении термометры получат имена, которые определены в файле config.py

    Доработанная версия проекта – в архиве Distiller.zip

2017-09-17_07-32-07.png
2017-09-17_07-32-07.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 814.2 Кб
ys1797 Доцент Санкт-Петербург 1K 339
Отв.172  18 Сент. 17, 01:01
Кстати, там в гуглчарт есть вертикальные термометры.
Я уже пробовал круглые - ну не информативны они. Красивые - да, и понтовые.
Всеж я для себя пришел к вертикальным "прямоугольникам" с задачей цвета.
Например, прогрев - синий.
Рабочая зона - зеленый.
Незначительные превышения - желтый.
Аварийные превышения или отсутствие данных - красный.

Видно и понятно без вникания в цифры.

C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.173  18 Сент. 17, 12:16
Погнали!
     Наконец, после проделанной подготовительной работы, можно приступить к приятным вещам: реализации процессов перегона.
    Собственно, этих процессов для моей универсальной колонны
Скрытый текст
apparat.jpg
Apparat. Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
всего два: перегон бражки и перегон спирта-сырца.
    Эти процессы совершенно схожи, различаются только параметрами, да ещё при перегонке спирта-сырца есть этап отбора голов. Этапы процесса перегонки спирта-сырца:
Smart Distiller (Умный дистиллятор с управлением по интернет)
Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.


    Отбор в моей колонне осуществляется в паровой фазе. Проходной дефлегматор колонны при этом, помимо своей основной функции, осуществляет функцию регулятора отбора паровой фазы. Можно задать такое охлаждение дефлегматору, что он будет конденсировать весь пар из куба (нулевой отбор).
    Для регулирования степени охлаждения дефлегматора на середине его длины установлен термометр. Показания этого термометра сравниваются с установленной температурой, и, при превышении установки, срабатывает клапан подачи охлаждающей воды в дефлегматор.
    Таким же образом производится регулирование степени охлаждения конденсатора. Отличается лишь тем, что температура срабатывания его клапана остается стабильной на протяжении всего процесса перегона.
    Такое решение позволяет не зависеть от температуры охлаждающей воды, стабильности её давления и минимизирует её расход.
    Регулятор охладителей реализован в модуле coolsRegulator.py пакета helpers в виде класса-потока. Он используется потоками, реализующими этапы процессов перегона.
    В процессе перегона, по мере уменьшения концентрации спирта, увеличивается температура пара, поступающего из куба (следовательно, и низа колонны). Более горячий пар начинает иссушать низ колонны, уменьшая её эффективную разделительную высоту. Для увеличения потока флегмы при увеличении температуры низа колонны увеличивается степень охлаждения дефлегматора. Регулирование температуры срабатывания клапана дефлегматора в зависимости от температуры низа колонны реализуется пропорциональным регулятором по формуле:
Tдеф=Tбаз-Kдеф×Tниз,
где Tбаз – базовое смещение температуры срабатывания;
   Kдеф - коэффициент регулирования дефлегматора.
Tбаз и Kдеф зависят от конструктивных особенностей колонны и определяются эмпирически.
    При увеличении степени охлаждения дефлегматора уменьшается отбор. Для компенсации увеличивается нагрев, также пропорциональным регулятором:
P=Pбаз+Kp×Tниз,
где Pбаз - базовое смещение мощности нагрева;
   Kp   - коэффициент регулирования нагрева.
Pбаз и Kp также определяются эмпирически на границе захлёба колонны.
    Параметры колонны хранятся в базе данных Distiller.bd в двух таблицах Parameters1 и Parameters2. При создании базы данных эти таблицы заполняются их файла config.py
    Закипание также определяется при скорости роста температуры на каком-либо термометре более 1°С.
    Этап стабилизации колонны необходим для её прогрева и распределения примесей по её высоте. При перегонке бражек (особенно пенистых) этот этап позволяет эффективно бороться с пеной. Мощность нагрева на этом этапе устанавливается такой, чтобы компенсировать теплопотери куба и колонны.
    На этапе отбора голов регулируется только мощность нагрева, температура срабатывания клапана дефлегматора устанавливается постоянной.
    Переход от отбора тела к отбору хвостов определяется только органолептически. Практически же спирт приемлемого питьевого качества составляет 1/3 от всего отбора тела. Остальные 2/3 идут на последующую перегонку.

    Доработанная версия проекта – в архиве Distiller.zip

2017-09-18_17-11-03.png
2017-09-18_17-11-03.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.

Distiller.zip 818.6 Кб
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.174  19 Сент. 17, 10:18
В продуктив, часть 1. Настройка ОС Raspbian
     Линуксообразная операционная система Raspbian для Raspberry PI 3 запускается с карты памяти микроSD, которая играет роль жёсткого диска одноплатного компьютера.
    Образ операционной системы Raspbian, упакованный в архив zip, скачиваем с сайта производителя (https://www.raspberrypi.org/downloads/).
    MicroSD Лучше использовать объемом 16…32 Гб класса 10. Уточнить тип карты можно тут. Карта microSD должна быть отформатирована в FAT32
Скрытый текстПохоже, что малинка не понимает другие форматы. MicroSD до 32 Гб можно отформатировать стандартными средствами Windows. 64 Гб можно отформатировать утилитой SmartDisk FAT32 Format
Мне "посчастливилось" купить на алиэкспрессе microSD Samsung 10 класса, которая страшно глючила и тормозила работу всего программного комплекса. Сейчас купил Kingston SDC10G2/16GB HXL6K-j8TTK2-FFW76, с этой картой всё работает без тормозов. Тем не менее, нужно минимизировать обращения к базам данных.
. Для подключения microSD к компьютеру, на котором она будет подготовлена, можно использовать USB-считыватель.
    Для записи образа Raspbian на микроSD используется инструмент Etcher (https://etcher.io/), есть версии для Mac OS, Linux и Windows. Скачиваем и устанавливаем. После его запуска указываем скачанный файл архива с образом Raspbian и нажимаем 'Flash!'.
    После записи (порядка 30 минут) можно извлечь считыватель и установить записанную микроSD в Raspberry PI.

    Настройка.
    Подключаем к Raspberry PI  USB-мышь и USB-клавиатуру, монитор подключается кабелем HDMI. Для питания платы Raspberry PI 3 Model B рекомендуется блок питания с напряжением 5V, способный обеспечить ток в нагрузке не менее 2A.
    После завершения автоматических настроек на экране увидим рабочий стол Raspbian.

    Подключение к Wi-Fi.
    Raspberry PI 3 имеет на борту WiFi модуль для беспроводной связи. Справа на панели Raspbian есть значок с двумя красными крестиками. Если на него нажать, появится список доступных сетей, из которых нужно выбрать свою домашнюю сеть. После ввода пароля осуществится подключение.

    Включение VNC-сервера и шины 1-Wire.
    Нажимаем на кнопку с малинкой, в вывалившемся меню выбираем Preferences->Raspberry PI Configuration. В открывшемся окне выбираем вкладку Interfaces и включаем (Enable) VNC и 1-Wire. После нажатия кнопки OK малинка потребует перезагрузку. После перезагрузки VNC и 1-Wire будут включены.
    1-Wire используется для доступа к цифровым термометрам DS18B20.
    VNC-сервер позволяет подключиться к рабочему столу Raspberry PI с другого компьютера удаленно через локальную сеть.

    Настройка статического IP.
    Работа сервера что VNC, что Web предполагает наличие статического IP-адреса у компьютера, на котором сервер запущен. Лучше сделать привязку IP-адреса малинки к её MAC-адресу на своем домашнем роутере ([сообщение #11976700]).

    Удалённое управление.
    Теперь можно отключить клавиатуру мышь и монитор от Raspberry PI и установить одноплатник в корпус своей системы автоматизации. Всё управление будем осуществлять удалённо с другого компьютера. Для этого скачиваем и устанавливаем на этом компьютере VNC-viewer (https://www.realvnc.com/en/connect/download/viewer/).
При запуске VNC-viewer нужно ввести статический IP-адрес, который мы присвоили Raspberry PI, затем логин pi и пароль raspberry. После этих шаманских действий нас вынесет прямо на рабочий стол малинки.
    При отключении HDMI-кабеля, эран минимизируется, поправить это можно, расскомментировав в файле /boot/config.txt (в командном окне sudo leafpad /boot/config.txt) строки:
framebuffer_width=1280
framebuffer_height=720
(можно задать 1600 на 900 или другие стандартные размеры дисплея). После перезагрузки рабочий стол будет приемлемых размеров.

    Локализация.
    Этот пункт не необходим для успешного вывода проекта в продуктив. Но если хочется, то настройка осуществляется на вкладке Localisation окна Raspberry PI Configuration.
2017-09-19_08-52-29.png
2017-09-19_08-52-29.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
SDreader.png
SDreader.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
PowerSuply.png
PowerSuply.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
Значёк.png
Значёк.png Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.
ys1797 Доцент Санкт-Петербург 1K 339
Отв.175  19 Сент. 17, 15:38
Еще один ресурс с виджетами. Приемущества - не нужен доступ к www.gstatic.com, можно работать в интранете.
Конкретно для термометров: https://www.jqwidgets.com/...gauge/index.htm
Общая демка: https://www.jqwidgets.com/jquery-widgets-demo/
HBB Кандидат наук Москва 357 94
Отв.176  19 Сент. 17, 16:11, через 33 мин
Линуксообразная операционная система Raspbian для Raspberry PI 3 запускается с карты памяти микроSD, которая играет роль жёсткого диска одноплатного компьютера.C-Bell, 19 Сент. 17, 10:18
То есть они поменяли Jessie на Stretch? Когда успели... Придется переустанавливаться.
После записи (порядка 30 минут) можно извлечь считыватель и установить записанную микроSD в Raspberry PI.C-Bell, 19 Сент. 17, 10:18
А Win32DiskImager работает, не пробовал? Он гораздо быстрее писал.
Подключаем к Raspberry PI  USB-мышь и USB-клавиатуру, монитор подключается кабелем HDMIC-Bell, 19 Сент. 17, 10:18
Можно отредактировать конфигурацию на PC через VM VirtualBox, и обойтись без периферии. Он нормально работает с линуксовской FS. Будет время, переустановлюсь и выложу как это сделать.
Всё управление будем осуществлять удалённо с другого компьютера. Для этого скачиваем и устанавливаем на этом компьютере VNC-viewerC-Bell, 19 Сент. 17, 10:18
Для удаленной работы начал использовать связку PuTTY + XMING по SSH. На мой взгляд, наиболее удачное сочетание, в отличие от VNC. Все летает и практически не глючит + более безопасная связь.
Inco Студент Донецк 47 10
Отв.177  20 Сент. 17, 11:11
Вопрос новичка в питоне. В последней версии проекта должны отрисовываться термометры?
У меня не рисуются, и я пока не знаю у меня этот бок или нет.
Причем одинаково не рисуются и в VS 2017 и в PyCharm.
Если должны рисоваться - буду рыть у себя.
C-Bell Научный сотрудник Улан-Удэ 1.8K 1.3K
Отв.178  20 Сент. 17, 12:10, через 60 мин
Должны отрисовываться термометры.
Не рисует только термометры, или указатель мощности тоже не рисуется?
Inco Студент Донецк 47 10
Отв.179  20 Сент. 17, 12:43, через 33 мин
Нет термометров и нагрева, вот так:
PyCharm.jpg
PyCharm.jpg Smart Distiller (Умный дистиллятор с управлением по интернет). Автоматика.