Начало работы с STM32 в Keil MDK-ARM

Статья посвящается Пановой Ксении.

Все картинки в этой статье кликабельны.

STM32 — это семейство 32-разрядных микроконтроллеров фирмы STMicroelectronics.

Микроконтроллеры содержат микропроцессорное ядро ARM, точнее ARM Cortex-M. Это ядро присуще не только микроконтроллерам STM32, оно существует само по себе, и на его основе выпускается множество микроконтроллеров от разных производителей.

Keil MDK-ARM (произносится «Кеил эм-ди-кей арм») — это среда разработки для микроконтроллеров с ядром ARM Cortex-M.

Читать далее Начало работы с STM32 в Keil MDK-ARM

Деплой Django-приложения + PostgreSQL с помощью docker-compose

Файл docker-compose.yml:

services:
  db:
    image: postgres
    volumes:
      - ./myapp_data/database:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    restart: always
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5

  web:
    build: .
    command: ./start-prod.sh
    volumes:
      - ./myapp_data/media:/usr/src/app/media
    ports:
      - "8002:8002"
    environment:
      - POSTGRES_NAME=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    depends_on:
      db:
        condition: service_healthy
    restart: always

Данные PostgreSQL будут храниться вне контейнера, в папке «./myapp_data/database».

У загружаемых файлов тоже внешнее хранилище – папка «./myapp_data/media».

Строчка «condition: service_healthy» задает условие, что контейнер с базой данных должен быть не просто запущен, но и база данных должна принимать соединения. Без этого условия веб-приложение запуститься раньше, чем база данных будет доступна.

Для создания образа веб-приложения (web) используется Dockerfile:

FROM python:3.11

WORKDIR /usr/src/app

# Копируем исходники с хоста (из текущей папки)
# в образ (в папку WORKDIR). 
COPY . .

# Устанавливаем зависимости веб-приложения.
RUN pip install --no-cache-dir -r requirements.txt

Веб-приложения запускается с помощью скрипта start-prod.sh:

#!/bin/bash -e

python manage.py collectstatic --noinput --clear

python manage.py makemigrations --noinput
python manage.py migrate --noinput

gunicorn -b '0.0.0.0:8002' my_app.wsgi:application

В файле настроек приложения (settings.py) нужно настроить подключение к базе данных:

import os

[...]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('POSTGRES_NAME'),
        'USER': os.environ.get('POSTGRES_USER'),
        'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
        'HOST': 'db',
        'PORT': 5432,
    }
}

Алгоритм нахождения целосчисленных решений уравнения a² + b² = c²

Нахождение таких трех целых чисел, при которых сумма квадратов двух целых чисел дает квадрат третьего целого числа.

Реализация на Python:

for i in range(1, 15):
    n = i*i
    for k in range(i+1, 15):
        m = k*k - n
        if m % 2 == 0:
            a = m / 2
            b = (n*(n+2*a))**0.5
            print (a, b, (a*a + b*b)**0.5)

Обоснование

Должно выполняться условие:

b^{2} = n (n + 2a)
a = \frac{k^2 - i^2}{2}
i = 1, 2, ...
k = i+1, i+2, ...
k^2 - i^2 – четное

Результат:

 a   b   c
-----------
 4   3   5
12   5  13
24   7  25
40   9  41
60  11  61
84  13  85
 6   8  10
16  12  20
30  16  34
48  20  52
70  24  74
96  28 100
 8  15  17
20  21  29
36  27  45
56  33  65
80  39  89
10  24  26
24  32  40
42  40  58
64  48  80
90  56 106
12  35  37
28  45  53
48  55  73
72  65  97
14  48  50
32  60  68
54  72  90
80  84 116
16  63  65
36  77  85
60  91 109
18  80  82
40  96 104
66 112 130
20  99 101
44 117 125
22 120 122
48 140 148
24 143 145
26 168 170

Системное программирование на языке Go

В системном программировании постоянно приходится производить манипуляции с битами и байтами. Именно о таких манипуляциях пойдет речь в данной статье.

Типы

В Go есть типы, которые позволяют точно задать размер числовой переменной:

  • uint8 – 1 байт
  • uint16 – 2 байта
  • uint32 – 4 байта
  • uint64 – 8 байт

Префикс «u» означает, что значение переменной беззнаковое, то есть 0, 1, 2 и так далее.

Без префикса «u» число будет знаковым, для int8 от -128 до 127. Отрицательные числа кодируются дополнительным кодом.

Типы int и uint лучше не использовать, так как на разных платформах они имеют разную длину, обычно 32 бита или 64 бита. Тип переменной нужно прописывать явно:

var x uint16 = 123

Если не указать тип uint16, то переменная будет типа int.

Поскольку тип uint8 хранит байт, то для этого типа есть синоним – byte. То есть byte и uint8 — это одно и тоже. Там, где нужно передать byte, можно передавать uint8, и наоборот, ошибки не будет.

Набор байт можно представить ввиде массива:

b := []byte{0, 1, 2, 3}

То есть многобайтовые последовательности можно хранить как в перменных типа uintN, так и в массивах. Но при использовании uintN нужно задуматься о порядке байт: little-endian или big-endian. Порядок байт зависит от платформы. Например, переменная типа uint16 содержит число, состоящее их 2-x байт, в памяти эти два байта могут располагаться по разному, в зависимости от платформы (процессора, операционной системы). При переборе ячеек памяти, младший байт может идти перед старшим, а может наоборот. Чаще всего младший байт идет вперед, такой порядок называется little-endian.

В пакете encoding/binary есть методы для преобразования uintN в массив байт и обратно.

Функции для преобразования числа (uint16) в массив байт:

  • binary.LittleEndian.Uint16([]byte) uint16
  • binary.BigEndian.Uint16([]byte) uint16
  • binary.NativeEndian.Uint16([]byte) uint16

Функции для преобразования массива байт в число (uint16):

  • binary.LittleEndian.PutUint16([]byte, uint16)
  • binary.BigEndian.PutUint16([]byte, uint16)
  • binary.NativeEndian.PutUint16([]byte, uint16)

Числа

Go позволяет записывать числа в двоичном и шестнадцетиричном формате. А так же в восьмеричном (но кому он нужен?).

Для удобства восприятия, разряды можно разделять подчеркиванием.

Примеры:

  • двоичное число: 0b1010_1111
  • шестнадцетиричное число: 0xF010_1234

Битовые операции

ОперацияПример
Иresult = a & b
ИЛИresult = a | b
Ислючающее ИЛИresult = a ^ b
Инверсияresult = ^a
И-НЕresult = a &^ b
Сдвиг влевоresult = a << b
Сдвиг вправоresult = a >> b

Эти операции в сочетании с присваиванием:

ОперацияЭквивалент
b &= ab = b & a
b |= ab = b | a
b ^= ab = b ^ a
b &^= ab = b &^ a
b >>= ab = b >> a
b <<= ab = b << a

В отличии от языка C в Go появилась новая операция – И-НЕ. Это операция И, но с инверсий второго аргумента, то есть «a И (НЕ b)». Такая операция часто используется для обнуления бита:

a &^= 1<<n

Пример. В результате следующей операции в переменной «a» биту под номером «n» будет присвоен 0.

a := 0b1111
a &^= (1 << 2) // Результат: 1011

Биты считаются с нуля справа налево.

Строковое представление

Различные байтовые и битовые представления можно выводить в консоль или получать ввиде строки, с помощью форматирования строк:

  • fmt.Printf(format, a, b, c) – печатает в консоль
  • fmt.Srintf(format, a, b, c) – возвращает строку

format – форматная строка, определяет формат результирующей строки.

fmt.Printf("%d %d %d", 1, 2, 3) // Напечатает "1 2 3"

В форматной строке можно использовать следующий подстановки:

  • %x – данные в шестнадцетиричном виде, буквы в нижнем регистре (a-f)
  • %X – данные в шестнадцетиричном виде, буквы в верхнем регистре (A-F)
  • %#b, %#x или %#X – аналогично предыдущи, но благодаря решетке, в начале добавляется префикс 0b, 0x или 0X соответственно.

С помощью этих подстановок можно выводить как обычные числа так и массивы:

fmt.Printf("%X", 0xAB) // Рузультат: "AB"
fmt.Printf("%X", []byte{0xAB, 0xCD}) // Рузультат: "ABCD"

Зафиксировать длину числа в строке и дополнить число ведущими нулями можно с помощью подстанвоки «%0w«, где w – длина строкового представления числа.

fmt.Printf("%04X", 0xAB) // Рузультат: "00AB"

Без нуля «0» строка будет дополняться пробелами:

fmt.Printf("%4X", 0xAB) // Рузультат: " AB"

Несколько сайтов на одном сервере с помощью Nginx и Docker

Допустим вы владеете сервером с белым IP-адресом 111.222.123.123. И хотите разместить на нем два сайта example.com и example.org. Такое размещение еще называется виртуальным хостингом.

В первую очередь, в настройках DNS сервера для доменов example.com и example.org нужно добавить записи A, которые будут ссылаться на IP-адрес вашего сервера.

Имя записиТип записиЗначение
example.comA111.222.123.123
example.orgA111.222.123.123

Чтобы все было серьезно, наши сайты будут работать по протоколу HTTPS. Для этого нам нужен сертификат. Его можно получить бесплатно от Let’s Encrypt. Для этого устанавливаем на сервер утилиту certbot. Как описано в официальной инструкции.

После установки утилиты, получаем сертификат следующей командой:

sudo certbot certonly --standalone -d example.com,example.org

Сертификат будет один на все домены. Можно было бы сделать разные сертификаты, но это усложнит настройку (подробнее).

Публичный и приватный ключ можно найти в папке /etc/letsencrypt/live/example.com.

Посмотреть сведения о сертификате и убедиться, что он для нескольких доменов, можно командой:

openssl x509 -noout -text -in /etc/letsencrypt/live/example.com/fullchain.pem

Находим строчки:

X509v3 Subject Alternative Name: 
DNS:example.com, DNS:example.org

А проверить срок действия сертификата можно командой:

openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/fullchain.pem

Установим Docker:

sudo apt install docker.io
sudo apt install docker-compose

Создадим папку для конфигурационных файлов и продолжим работать в ней:

mkdir webserver
cd webserver

Нам понадобится несколько докер-контейнеров. Проще всего создать их с помощью docker-compose. Создадим конфигурационный файл docker-compose.yml со следующим содержимым:

version: "3"

services:
  webserver:
    image: nginx
    ports:
      - 80:80
      - 443:443
    # При перезагрузке сервера, контейнер запуститься автоматически.
    restart: always
    volumes:
      # Пробрасываем в контейнер файл конфигурации Nginx.
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      # Пробрасываем папку для certboot, чтобы обновлять сертификат методом webroot.
      - ./certbot:/var/www/html/certbot
      # Пробрасываем в контейнер ключи Let's Encrypt.
      - /etc/letsencrypt/live/example.com/fullchain.pem:/etc/nginx/certs/fullchain.pem:ro
      - /etc/letsencrypt/live/example.com/privkey.pem:/etc/nginx/certs/privkey.pem:ro
    
    # Перед запуском Nginx нужно запустить отдельные сайты.
    depends_on:
      - example-com
      - example-org

  example-com:
    build: example-com/

  example-org:
    build: example-org/

С помощью такого конфига, docker-compose создаст три контейнера, то есть три сервера, которые смогут взаимодействовать друг с другом. webserver, example-com и example-org – это доменные имена, с помощью которых сервера могут обращаться друг к другу. Например, http://example-com:8000/.

Теперь создадим конфигурационный файл для Nginx, nginx.conf, со следующим содержимым:

nginx.conf
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    # Обработка запросов по HTTP.
    server {
        listen 80;
        server_name _;

        # Для обновления сертификата с помощью certbot методом webroot.
        location /.well-known/acme-challenge {
            root /var/www/html/certbot;
            allow all;
        }
        
        # Перенаправление HTTP на HTTPS.
        location / {
            return 301 https://$host$request_uri;
        }
    }

    # Предотвратить обработку HTTP-запросов, если в запросе не указан домен.
    server {
        listen 80;
        server_name "";
    }

    # Специфика протокола SSL не позволяет использовать разные сертификаты,
    # поэтому сертификат один на все домены.
    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # предотвратить обработку HTTPS-запросов, если в запросе не указан домен.
    server {
        listen      443 ssl;
        server_name "";
        return      444;
    }

    # В соответствии с доменом, передаем запрос на сервер конкретного сайта.

    server {
        # Внешний порт – 443 (стандартный порт для HTTPS).
        listen 443 ssl;
        
        # Внешний домен.
        server_name example.com;
        
        location / {
            # Протокол HTTP так как сервер доступен только из внутренней сети.
            # example-com – домен сервера, который мы прописали в docker-compose.yml.
            # 8000 – порт сервера
            proxy_pass http://example-com:8000;
        }
    }

    server {
        listen 443 ssl;
        server_name example.org;
        location / {
            proxy_pass http://example-org:8000;
        }
    }
}

Но у нас пока нет сайтов. Создадим заглушки для них.

mkdir example-com
mkdir example-org
nano example-com/Dockerfile
nano example-org/Dockerfile

В Dockerfile-файлы поместим следующий код:

# Создать контейнер из образа python:3.10.
FROM python:3.10

# После создания контейнера, запустить в нем простейщий веб-сервер на порту 8000.
CMD ["python", "-m", "http.server", "8000"]

На этом конфигурирование завершено, осталось собрать и запустить сервера:

docker-compose build
docker-compose up -d

Опция -d запускает в фоновом режиме.

Остановить и удалить сервера можно командой:

docker-compose down

Let’s Encrypt выдает сертификат на 3 месяца. Для обновления сертификата нужно выполнить команды:

# Переходим в рабочую папку
cd webserver
# Обновляем сертификат
certbot certonly --webroot -w certbot -d example.com,example.org
# Перезапускаем nginx
docker-compose down
docker-compose up -d

Есть способ настроить автоматическое обновление сертификата, но над ним нужно еще подумать…

Автоматическое форматирование C++ кода в Qt Creator с помощью clang-format

Устанавливаем clang-format на Ubuntu из основного репозитория:

sudo apt install clang-format

Или свежую версию из репозитория LLVM:

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt update
sudo apt install clang-format-12

Запускаем Qt Creator. Идем «Справка -> О модулях…«. Ставим галочку на Beautifier.

Идем в «Инструменты -> Параметры…«.

Можно включить форматирование при сохранении файла.

На вкладке Clang Format указываем путь к clang-format.

Можно использовать стандартный стиль, а можно создать собственный.

Стиль сохраняется в домашнюю папку по следующему пути:

$HOME/.config/QtProject/qtcreator/beautifier/clangformat/my_style/.clang-format

Допустим у вас уже есть файл .clang-format, который хранится в репозитории вашего проекта. Тогда можно создать символическую ссылку на него:

rm $HOME/.config/QtProject/qtcreator/beautifier/clangformat/my_style/.clang-format

ln -s my_project/.clang-format $HOME/.config/QtProject/qtcreator/beautifier/clangformat/my_style/.clang-format

Можно настроить горячие клавиши для форматирования:

Генплан Казани 2020 (до 2040 года), проект Аметьево

Открыть крупномасштабные карты плана.

Группа Аметьево в телеграме.

Проект Аметьево (Калуга) от 2021 года

15 апреля 2021 года было опубликовано постановление об общественных обсуждениях по новому проекту Аметьево.

В этом документе находится новый проект планировки территории Аметьево с двумя чертежами, описанием проекта и планом работ.

Чертеж 1

Чертеж 2

Генплан

В документе опубликованы 2 чертежа и описание проекта.

Генплан и вся связанная с ним информация размещена в разделе Градостроительство на сайте мэрии Казани.

Ниже приведены выжимки из плана, связанные с Аметьево.

Кликайте на картинки, чтобы увеличить.

Карта планируемого размещения линейных объектов транспортной инфраструктуры местного значения (линейные объекты — это дороги):

Обозначения:

Основной чертеж Генерального плана городского округа Казань (карта функциональных зон):

Обозначения:

Описание генплана находится в документе «Положение о территориальном планировании» (Приложение к решению №5-38.pdf). Этот документ состоит почти из 800 страниц.

Генеральный план г. Казани разработан на следующие проектные периоды:

  • первая очередь строительства (до 2025 года);
  • расчетный срок – на 20 лет (до 2040 года);
  • перспектива – на 35 лет (до 2055 года).

На карте с линейными объектами пунктирными линиями обозначены новые дороги, для них есть условные названия «Проезд №». Более подробная информация о проездах содержится в приложении указанном выше. Проезд №79 — страница 389. Проезд № 116 — страница 416.

Как удалить процедуру или функцию из базы данных

При разработке инструментов, которые взаимодействуют с разными СУБД, приходится учитывать много нюансов, потому что одни и теже вещи реализованы по-разному и стандарт SQL поддерживается по-разному. Если рассмотреть достаточное количество СУБД, то окажется, что нет универсального способа удалить функцию или процедуру.

Читать далее Как удалить процедуру или функцию из базы данных

Добавление настроек к отдельным файлам в CMake

Настройка целей

CMake поддерживает следующие команды для добавления настроек к цели:

  • target_compile_definitions — добавляет определения,
  • target_compile_options — добавляет опции компилятора,
  • target_include_directories — добавляет пути поиска заголовочных файлов,
  • target_link_libraries — добавляет линкуемые библиотеки.

Эти команды принимают имя цели в качестве первого аргумента. Цели создаются командами add_executable и add_library. Для add_executable целью является исполняемый файл, а для add_library — библиотека (статическая или динамическая).

CMake поддерживает следующие команды для добавления глобальных настроек:

  • add_compile_definitions — добавляет определения,
  • add_compile_options — добавляет опции компилятора,
  • include_directories — добавляет пути поиска заголовочных файлов.
  • link_libraries — добавляет линкуемые библиотеки.

Эти настройки применяются к целям в текущей папке и всех вложенных папках.

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

set_property

Чтобы добавить настройки к исходному файлу, можно воспользоваться командой set_property или set_source_files_properties. Пример использования:

set_property(SOURCE LibPQExt.cpp APPEND PROPERTY INCLUDE_DIRECTORIES
    ${CMAKE_SOURCE_DIR}/libs/postgresql-9.6/src/include
)

set_property позволяет добавлять настройки для нескольких исходных файлов одновременно.

Если использовать опцию APPEND, то настройка добавляются к уже существующему списку настроек.

Объектная библиотека

Еще один способ — создать из исходного файла или их набора специальную цель, которая называется объектной библиотекой.

Синтаксис команды для создания объектных библиотек:

add_library( OBJECT ...)

К такой цели можно применять команды с префиксом target_. Например, можно добавить определение LOGGER_TYPE=3 только для файла logger.cpp. Делается это следующим образом

add_library(logger OBJECT logger.cpp)
target_compile_definitions(logger PUBLIC LOGGER_TYPE=3)

Объектную библиотеку можно добавить к другим целям как набор исходных файлов. Для этого в команды add_executable или add_library надо добавить выражение $, где objlib — имя объектной библиотеки. Например:

add_library(logger OBJECT logger.cpp)
target_compile_definitions(logger PUBLIC LOGGER_TYPE=3)

add_executable(my_application
    main.cpp
    archiver.cpp
    $
)

Поддержка объектных библиотек была введена в CMake версии 2.8.8 (апрель 2012 года). При использовании объектных библиотек рекомендуется в файле CMakeLists.txt ограничить версию CMake на уровне 2.8.8 или выше:

cmake_minimum_required(VERSION 2.8.8)

Комиссии на Etsy и Ebay

Размер комиссии в валюте рассчитывается по следующей формуле.

\left( P\cdot\frac{E1}{100} + E2 \right) \cdot \left( \frac{V}{100} + 1 \right)

(P * E1/100 + E2) * (V/100 + 1)

Где:

  • P — цена товара (вместе с доставкой), валюта;
  • E1 — комиссия магазина от цены товара, %;
  • E2 — фиксированная комиссия магазина, валюта;
  • V — российский НДС, %.

Примечания:

  • НДС взимается не с цены товара, а с комиссии магазина.
  • Фиксированная комиссия на Ebay равна 0.
  • Комиссия Ebay составляет 10 % от цены товара.
  • Фиксированная комиссия на Etsy составляет 0,2 доллара США и берется за публикацию товара.
  • Комиссия Etsy составляет 5 % от цены товара.

Настройка общей папки в VirtualBox

В статье приводится способ настройки общей папки, когда на виртуальной машине установлен Linux, в частности Ubuntu 18.04.

Первым делом нужно установить дополнения гостевой ОС.

Подключение образа дополнений гостевой ОС в VirtualBox
Подключаем образ диска дополнений гостевой ОС.
Запускам установщик дополнений в появившемся окне.

В настройках виртуальной машины нужно выбрать общие папки. Нужно поставить галочки «Авто-подключение» и «Создать постоянную папку.»

VirtualBox выбор общей папки

После сохранения настроек, на рабочем столе виртуальной машины появится папка с префиксом sf_. Это и есть общая папка.

Но пока в эту папку нет доступа. Чтобы доступ появился, нужно добавить своего пользователя в группу vboxsf. Это делается в терминале с помощью следующей команды.

sudo usermod -a -G vboxsf <USERNAME>

Где вместо <USERNAME>, нужно указать ваше имя пользователя.

Доступ в папку откроется после перезагрузки виртуальной машины.