воскресенье, 31 октября 2021 г.

Создаем GTK список (виджет)

 Приведу без комментариев код. 

// gtktable.c
#include <stdio.h>
#include <gtk-3.0/gtk/gtk.h>

GtkWidget* main_window;
GtkWidget* box1;
GtkWidget* scroll_window;

GtkWidget* list_widget;
GtkTreeStore* store_list;
enum{COL1, COL2, COL3, NCOL};
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection* selection;
GtkTreeIter iter1;

gboolean
selection_func (GtkTreeSelection *selection,
                     GtkTreeModel     *model,
                     GtkTreePath      *path,
                     gboolean          path_currently_selected,
                     gpointer          userdata)
{
    g_print ("selection_func is called.\n");
    return TRUE;
}

void
create_gtk_table(){
    store_list = gtk_tree_store_new(NCOL, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
    list_widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store_list));
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_widget));
    gtk_tree_selection_set_select_function(selection, selection_func, NULL, NULL);

    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list_widget), TRUE);
    gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(list_widget),GTK_TREE_VIEW_GRID_LINES_BOTH);
    gtk_container_add(GTK_CONTAINER(scroll_window), list_widget);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(G_OBJECT(renderer), "background", "grey", NULL);
    g_object_set(G_OBJECT(renderer), "foreground", "black", NULL);

    column = gtk_tree_view_column_new_with_attributes("Title1",renderer, "text", COL1, NULL);
    gtk_tree_view_column_set_clickable(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title2",renderer, "text", COL2, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title3",renderer, "text", COL3, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    g_object_unref(G_OBJECT(store_list));

    for(gint i = 0 ; i < 100; i++){
      gtk_tree_store_append(store_list, &iter1, NULL);
      gtk_tree_store_set(store_list, &iter1, COL1, i+1, COL2, "string ?", COL3, "the string!", -1);
    }
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
    main_window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (main_window), "The Table in The Scrolling");
    gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 500);
    gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

    box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
    gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
    gtk_container_add (GTK_CONTAINER (main_window), box1);

    scroll_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_set_size_request(scroll_window, 350, 450);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    gtk_box_pack_start(GTK_BOX (box1), scroll_window, FALSE, FALSE, 10);

    create_gtk_table();

    gtk_widget_show_all (main_window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;
    app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return status;
}

Код, который ответственнен за создание списка находится в функции  create_gtk_table. После выполнения эта программа выводит на дисплей вот такое:

Если для компиляции ипользовать CMake, то необходимо создать файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 3.18)

project (gtktable)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(gtktable gtktable.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (gtktable ${GTK3_LIBRARIES})

Компилировалось и выполнялось на Debian Bullseye. Собственно, это все, что хотел сказать.

четверг, 14 октября 2021 г.

Простейшее испльзования систем CMake и Meson на примере gtk+ приложения

 Напишем простое gtk приложение, которое считает sin от вводимой величины. 

//   calc_sin.c
#include <gtk/gtk.h>
#include <math.h>

GtkWidget* main_window;
GtkWidget* box1;

GtkWidget* edit1;
GtkWidget* button1;
GtkWidget* label1;
GtkWidget* button_exit;

void
calc_sin(){
    double result = sin(atof(gtk_entry_get_text(GTK_ENTRY(edit1))));
    gchar *display = g_strdup_printf("%f", result);   
    gtk_label_set_text (GTK_LABEL(label1), display);
    g_free(display); 
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
      main_window = gtk_application_window_new (app);
      gtk_window_set_title (GTK_WINDOW (main_window), "The sin");
      gtk_window_set_default_size (GTK_WINDOW (main_window), 200, 400);
      gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

      box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
      gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
      gtk_container_add (GTK_CONTAINER (main_window), box1);

      edit1=gtk_entry_new();
      gtk_box_pack_start(GTK_BOX (box1), edit1, FALSE, FALSE, 10);

      button1 = gtk_button_new_with_label("Calculate!");
      g_signal_connect (button1, "clicked", G_CALLBACK (calc_sin), NULL);
      gtk_box_pack_start(GTK_BOX (box1), button1, FALSE, FALSE, 10);

      label1 = gtk_label_new("result");
      gtk_box_pack_start(GTK_BOX (box1), label1, FALSE, FALSE, 10);

      button_exit = gtk_button_new_with_label ("Exit");
      gtk_box_pack_start(GTK_BOX (box1), button_exit, FALSE, FALSE, 10);
      g_signal_connect_swapped (button_exit, "clicked", G_CALLBACK
(gtk_widget_destroy), main_window);

      gtk_widget_show_all (main_window);
}

int
main (int    argc,
      char **argv)
{
    GtkApplication *app;
    int status;
      app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
      g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
      status = g_application_run (G_APPLICATION (app), argc, argv);
      g_object_unref (app);
      return status;
}

 А теперь откомпилируем-отлинкуем этот код разными методами и выполним его. Предполагается, что cmake и meson уже установлены.

Сначала сделаем это "традиционно":

#gcc calc_sin.c `pkg-config  --cflags --libs  gtk+-3.0` -lm -o calc_sin_bin

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

В едитбокс вводим величину в радианах, нажимаем на кнопочку "Calculate!" и вместо слова "result" увидим величину синуса введенного значения.

Теперь соберем нашу программу с помощью CMake.
Создадим файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 2.6)

project (calc_sin_prj)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(calc_sin_bin calc_sin.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (calc_sin_bin ${GTK3_LIBRARIES} m)

Этот файл в данном случае должен быть в директории с нашим исходником. Далее делаем такую последовательность команд:

#mkdir cmakebuilddir
#cd cmakebuilddir
#cmake ..
#cmake --build .

Теперь соберем нашу программу с помощью meson.
Создадим файл meson.build следующего содержания:

project('calc_sin_meson', 'c')
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required:false)
deps = [dependency('gtk+-3.0'), m_dep]
executable('calc_sin_bin', 'calc_sin.c', dependencies : deps)

 Делаем такую последовательность команд:

#meson setup mesonbuilddir
#cd mesonbuilddir
#meson compile

Во всех трех случаях получаем бинарник  calc_sin_bin с одинаковою функциональностью. Обратите внимание, что этот бинарник создается в случае meson в директории mesonbuilddir , а в случае CMake в директории  cmakebuilddir. Получается что дерево исходников находится отдельно, а все рабочие файлы каждой системной сборки (мезон и симэйк) находятся только в своей поддиректории соответственно. Аккуратно и логично.

суббота, 9 октября 2021 г.

Навигация по документу в vim

    Заезженная тема, но достойная отдельного рассмотрения. Рассмотрим команды перемещения по открытому документу в редакторе vim в нормальном режиме (есть еще режим редактирования, визуальный режим и режим ввода команд после двоеточия). Команды интуитивно сгруппированы в отдельные таблицы и параграфы.
 

аналогия "обычных" клавиш "стрелка вверх" и "стрелка вниз" курсор относительно текста не двигается, текст смещается на одну строку курсор относительно текста не двигается, текст смещается на полэкрана текст смещается на полный экран
вверх Ctrl-p
Ctrl-y Ctrl-u
Ctrl-b
вниз Ctrl-n Ctrl-e Ctrl-d
Ctrl-f

 Ctrl-y и Ctrl-e полезно запомнить, так как эти комбинации имеют полезную функциональность в режиме редактирования: копируют букву в текущей позиции в верхнюю или нижнюю строку соответственно.

текст не перемещаетсяотносительно окна, двигается только курсор
 текст перемещается относительно окна, курсор привязан к позиции в тексте
влево h

вниз j, на дно экрана L
вниз экрана zb
вверх k, вверх экрана H
вверх экрана zt
вправо l

в середину экрана M
в середину экрана zz z.

 

"Глобальные" перемещения:

gg идем в начало;
G
идем в самый конец;
50%
идем в середину всего документа (50% же);
:50
идем на 50-ю строку;

  ctrl-o, ctrl-i перемещаемся вперед-назад по сохраненной истории координат курсора. Не все положения курсора сохраняються в этой истории. Сохраняються координаты, в которые мы попадали в результате перемещения, которое классифицируется как "прыжок". Список сохраненных прыжков можно увидеть командой :jumps. Очень полезная фишка. Вам не нужно бегать по файловому менеджеру в поисках файла, который вы уже когда-то редактировали. Просто войдите в vim в нормальном режиме и понажимайте  ctrl-o, ctrl-i.

Строчные перемещения:

0
переходим в самое начало строки;
^
переходим в на первый непробельный символ строки;
$
переходим в самый конец строки;
-
переходим на начало строки ввех;
+
переходим на начало строки вниз;

Пословные перемещения:

w
переход к началу следующего слова;
W
переход к началу следущего слова;
e
переход к следующему концу слова;
E
переход к следующему концу слова;
b
переход к началу предыдущего слова;
B
переход к началу предыдущего слова;
tx, fx
переход к следующей букве х;
Tх, Fх
переход к предыдущей букве х;
;
повторить предыдущий tfTF-переход;

Ну а про эти команды мне даже как-то неудобно и говорить:
mx установить закладку х;
'x перейти к закладке х;
''
переходим к предыдущему месту, откуда делали последний переход к закладке;
:marks смотрим какие у нас закладки;

Учите клавиатуру. Воздастся. ))


среда, 5 мая 2021 г.

Конфигурирование связки SPF, DKIM, DMARC записей и sendmail на debian

    Что такое SPF, DKIM, DMARC объяснять не буду. Раз вы это читаете, то вы уже знаете, что это или хотя бы зачем. Тут просто расскажу как я это установил на debian buster c почтовым сервером sendmail. 

#apt install opendkim opendkim-tools

#vim /etc/opendkim.conf

Syslog                         yes
SyslogSuccess            Yes
LogWhy                      Yes
AutoRestart                Yes
AutoRestartRate        10/1h
UMask                        002
Canonicalization        relaxed/simple
Mode                          sv
Socket                        inet:8891@127.0.0.1
PidFile                       /var/run/opendkim/opendkim.pid
SignatureAlgorithm   rsa-sha256
UserID                       opendkim:opendkim
Domain                       my.domain.ua
Selector                      mymail

OversignHeaders       From
TrustAnchorFile         /usr/share/dns/root.key
ExternalIgnoreList     refile:/etc/opendkim/TrustedHosts
InternalHosts             refile:/etc/opendkim/TrustedHosts
KeyTable                     refile:/etc/opendkim/KeyTable

SigningTable               refile:/etc/opendkim/SigningTable

Обратите внимание на пять строчек, выделенных жирным шрифтом. Их понимание, на мое мнение, особенно важно для того, что мы хотим сделать.

вместо my.domain.ua подставим вывод команды  (на почовом сервере)

#hostname -d

вместо mymail подставим вывод команды (также на почтовике) 

#hostname 

Далее создаем директорию, где находится пара приватный-публичный ключи:

#mkdir -p /etc/opendkim/keys

И создаем собственно эти ключи:

#opendkim-genkey -D /etc/opendkim/keys -d my.domain.ua  -s mymail
#chown -R opendkim:opendkim /etc/opendkim/keys
#chmod 640 /etc/opendkim/keys/mymail.private
#chmod 644 /etc/opendkim/keys/mymail.txt

mymail.private - здесь хранится приватный ключ, доступ к которому имеет только почтовый демон (в нашем случае - сендмейл).
mymail.txt - здесь хранится публичный ключ, который мы впишем в файл зоны днс.
    Далее создаем и редактируем три таких файла:

#vim /etc/opendkim/KeyTable

mymail._domainkey.my.domain.ua my.domain.ua:mymail:/etc/opendkim/keys/mymail.private
Здесь мы говорим мы говорим мильтеру opendkim, а значит сендмейлу, где находится приватный ключ.

#vim /etc/opendkim/SigningTable

*@my.domain.ua mymail._domainkey.my.domain.ua
Здесь мы говорим какие почтовые адреса соответствуют dkim-записи в днс.

#vim /etc/opendkim/TrustedHosts

127.0.0.1
localhost
my.domain.ua
Тут имя файла говорит само за себя.
 
    Рестартуем опендким и смотрим, слушает ли он порт, что мы прописали в конфиге выше:
#systemctl restart opendkim
#netstat -nlp | grep 8891
tcp        0      0 127.0.0.1:8891          0.0.0.0:*               LISTEN      23116/opendkim

    Расскажем сендмейлу, что у него появился новый мильтер (о мильтере можно думать как о некоем плагине, который подсоединяется к главному процессу сендмейла с помощью специального API - milter API). Редактируем текстовый конфиг сендмейла:
#vim /etc/mail/sendmail.mc
...

INPUT_MAIL_FILTER(`milter1', `...')dnl
INPUT_MAIL_FILTER(`milter2', `...')dnl
INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@127.0.0.1')dnl
define(`confINPUT_MAIL_FILTERS',`opendkim,milter1,milter2')dnl
...

milter1 и milter2 вписаны для примера. Их может и не быть или может быть больше двух. У меня, например, к сендмейлу подсоединены два мильтера кламава и спамассассина. Третий стало быть опендким. Обратите внимание на `S=inet:8891@127.0.0.1' - эта запись должна совпадать с тем, что мы прописали в конфиге выше.

Выполняем

#sendmailconfig
#systemctl restart sendmail 

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

$ORIGIN         my.domain.ua.
@                     IN SOA  mydns.my.domain.ua. hostm.my.domain.ua. ( ... )
                        IN      NS      mydns.my.domain.ua.
                        IN      MX      10 mymail.my.domain.ua.
@                     IN      TXT     "v=spf1 +mx +a ip4:11.11.11.11 -all"
mymail._domainkey        IN    TXT    "v=DKIM1\; h=sha256\; k=rsa\; p=""xxxx""xxxx""xxxx""xxxx" 
_adsp._domainkey        IN    TXT    "dkim=all"
_dmarc                        IN    TXT    "v=DMARC1\; p=reject\; rua=mailto:hostm@my.domain.ua"

    11.11.11.11 - айпишник нашего почтовика. Строка

mymail._domainkey        IN      TXT     "v=DKIM1\; h=sha256\; k=rsa\; p=""xxxx""xxxx""xxxx""xxxx"

полностью соответствует содержимому файла mymail.txt, что мы создали ранее. ххххххх - так обозначены цифры ключа. Есть пара нюансов. Точки с запятой в файле днс соответствуют началу комментариев. Поэтому мы их экранируем слэшем. Также, строки в файле зоны днс не могут быть длиннее кажется 256 символов, что меньше, чем длина нашей строки (строка, что начинается из v= ), из-за чего эту длинную строку надо разбить несколькими вставками "" - они обозначены синим цветом. Также длинные строки можно заключать в круглые скобки, но у меня почему-то это не работало.

    Строка с v=spf1 и есть spf запись. Строка с v=DKIM1 и есть dkim запись. Строка с v=DMARC1 и есть dmarc запись. Две строки, начинающиеся с _adsp и _dmarc просят всех не принимать письма без DKIM-подписи нашего домена. Собственно для этого все это и затевалось.

    Теперь при отсылке почты в логах почтовика появяться соответствующие строки:

# journalctl -u opendkim

DKIM-Signature field added (s=mymail, d=my.domain.ua)

    Также в служебной информации писем появяться DKIM-подписи вашего домена.

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

воскресенье, 17 января 2021 г.

Sendmail: анализ логов

    Отчаявшись найти хотя б даже и неинтуитивный анализатор логов почтового сервера на Sendmail и Dovecot, пришлось осилить несколько строк командной оболочки. Дело было в Дебиан.

#journalctl --since yesterday -u sendmail|grep ctladdr|grep Sent|awk -F: '{print $5}'|grep -Po 'ctladdr=<\K[^>]*' 

Это смотрим отправщиков писем.

#ls -al /var/mail/|awk '{printf("%-20s%s\n", $3,$5)}'

Это анализируем почтовые ящики.

#journalctl -u dovecot|grep pop3-login|egrep -v Disconnected|awk -F, '{print $1}'|awk -v RS='<' -v FS='>' 'NR>1{print $1}'

Это анализируем читателей писем.


sort|uniq -c|sort -nk1 такое добавляем в конец пайпа для сортировки вывода по числам в первом столбце.

awk '{sum+=$2} END {print $2}' такого типа команду добавляем в конец пайпа, чтоб просуммировать числа во втором столбце.

Ну и напоследок формат по умолчанию записи в журнал сендмейлом в случае если отсылка мыла произошла успешно (схематично):

Date Time MyServerName sm-mta[x]: 10HF0kYP020730: from=<g@g.org>, size=x, class=0, nrcpts=1, msgid=<xxx>, proto=ESMTPSA, daemon=MTA, relay=server.domen [x.x.x.x]
Date Time MeServerName sm-mta[x]: 10HF0kYP020730: to=<g2@g2.org>, ctladdr=<g@g.org> (1254/1498), delay=00:00:02, xdelay=00:00:02, mailer=esmtp, pri=120563, relay=x.net. [x.x.x.x], dsn=2.0.0, stat=Sent (ok dirdel)



 

 

четверг, 31 декабря 2020 г.

git: обобщенная схема команд

 

Наиболее частые команды:

git init
git add .
git rm file.txt
git rm --cached file.txt
git mv old_name new_name
git rm \*~

git config --global user.email "your@mail.address.org"
git config --global user.name "you_name"
git config --global --add https.proxy https://10.10.0.1:3128
git config --list
git config --global --unset http.proxy
git config --list | grep proxy

git commit -m "some comment"
git commit -am "commit with all changed files"
git remote add master  https://your_name@bitbucket.org/_/repository_name.git
git remote -v //To list the existing remotes
git push -f master master
git remote rm master

git status //делайте почаще эти команды
git status -s
git log

git reset --hard <hash>
git reset --hard origin/master

git branch
git checkout -b newbranch
git checkout newbranch
git merge newbranch (делать, если находимся на ветке master)

git diff
git diff --staged

воскресенье, 13 декабря 2020 г.

Основные команды для работы с пакетами в Debian

  • dpkg -i пакет.deb проинсаллировать пакет
  • dpkg -r пакет (без расширения  deb) удалить пакет, исключая файлы конфигурации
  • dpkg -P пакет (без расширения  deb)полностью удалить пакет
  • dpkg -l список всех проинсталлированных пакетов
  • dpkg -l пакет (без расширения  deb) вывод короткой информации про отдельный пакет
  • dpkg -L (без расширения  deb) вывод всех файлов проинсталлированных из данного пакета
  • dpkg -c пакет.deb список файлов в еще непроинсталлированном пакете
  • dpkg -S /path/to/file показывает какому пакету принадлежит файл
  • dpkg -p пакет (без расширения  deb) вывод детальной информации про отдельный пакет
  • dpkg -s пакет (без расширения  deb) проверяет, установлен ли пакет

суббота, 14 декабря 2019 г.

Бэкап на коленке: утилита expect

Задача: несколько серверов и один комп-архиватор, на котором нужно делать бекап "избранных" файлов. На каждом сервере создаем юзера archiver (ну или какой вам нравиться). В домашнем каталоге у него (на каждом  же сервере) создаем папочку  backup (ну или какое название вам нравиться другое). Также создаем скрипт, который кроном запускается с нужной периодичностью и пакует в эту папку нужный вам контент.
На компе-архиваторе создаем папку backups с подкаталогами у которых имена сопадают  с именами архивируемых серверов. Далее устанавливаем утилиту expect на компе-архиваторе и пишем скрипт такого содержания:

#!/usr/bin/expect -f
set ret [ catch {

array set user {
        server_1        archiver
        server_2        archiver
# etc
        server_n         archiver
}

#можно и без этого абзаца, но вдруг find на серверах находится в разных местах
array set find {
        server_1          /usr/bin/find
        server_2          /usr/bin/find
# etc
        server_n         /usr/bin/find
}

array set ips_passwords {
        server_1          password1 
        server_2          password2 
# etc
        server_n          passwordn
}

set timeout -1

send_user "backup is starting!...\n"
foreach keyip [array name ips_passwords] {
        #
        #Копируем периодические бэкапы на наш комп-архиватор
        spawn /usr/bin/scp $user($keyip)@$keyip:/home/$user($keyip)/backup/* /home/backuper/backups/$keyip
        match_max 100000
        expect  "*?assword*:"
        send -- "$ips_passwords($keyip)\r"
        send -- "\r"   
        expect eof
        #
        #Со следующим абзацем поосторожней. Удаляет в директории, где храняться
        #наши бэкапы файлы старше 5 дней.
        spawn /usr/bin/ssh $user($keyip)@$keyip $find($keyip) /home/$user($keyip)/backup -ctime +5 -exec /bin/rm \{\} \\\;
        match_max 100000
        expect "*?assword*:*"
        send -- "$ips_passwords($keyip)\r"
        send -- "\r"
        expect eof
}
send_user "backup had finished succesfully!\n"

} result ]

set fh [open /home/backuper/Desktop/expect-works w]
puts $fh "Code $ret, Message/Result $::result \n"
close $fh


Нюанс. Сделайте, если еще не делали, логин юзером archiver с вашего компа-архиватора через ssh на каждый сервер. Эту ситуацию (первая сессия ssh archiver@server_x) можно отработать и в скрипте, но тогда код усложниться. А нам нужна проcтота. Не так ли?

среда, 4 декабря 2019 г.

Работа с com портом из командной строки Linux

Вывод параметров порта в читаемом виде:
#stty -a -F /dev/ttyUSB0
Переключение в raw режим:
#stty -F /dev/ttyUSB0 raw -echo -echoe -echok
Послать команду:
#echo "HELLO" > /dev/ttyUSB0
#echo  -ne '\x23\x02\x01\x00\x50\x01\r' > /dev/ttyUSB0
Прочитать данные:
#cat /dev/ttyUSB0
Сохранить параметры порта для последующего восстановления:
#stty -g -F /dev/ttyUSB0 > save.txt
#stty --save /dev/ttyUSB0 > save.txt
Считываем сохраненные параметры:
#stty -F /dev/ttyUSB0 `cat save.txt`
Устанавливаем скорость 115200 бод, 8 битов данных, стоп бит, без проверки четности (режим 8N1):
#stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb


среда, 23 октября 2019 г.

Вызов команд Gnuplot из программы С++

    Gnuplot, как известно, интерактивная программа. Также в ней существует возможность создавать скрипты, которые можно запускать с командного интепретатора операционной системы. Иногда бывает удобно "заворачивать" эти команды в программу с/с++. Далее в тексте приведены две таких программы. Кто умеет пользоваться Gnuplot-ом без труда сможет изменить их текст для своих нужд. Первая програма выполняет стандартную задачу постройки 2D-графиков, а вторая строит "карту" из данных, представленых матрицей. В обоих случаях вывод происходит не окно, а в отдельный jpeg файл.
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
using namespace std;

int main(){
    FILE* gnuplotpipe;
    string command;
    gnuplotpipe = popen("gnuplot -persist","w");
    if(!gnuplotpipe)
        cerr<<("there are no gnuplot");

command =
    "set terminal jpeg size 3400,2300 font 'Times New Roman-bold,48';"
    "set output 'graphic.jpg';"
    "plot "
    "'data1.dat' u 1:2 w l t '1', "
    "'data2.dat' u 1:2 w l t '2', "
    "'data3.dat' u 1:2 w l t '3'; ";

    fprintf(gnuplotpipe,"%s",command.c_str());
    fflush(gnuplotpipe);


    fprintf(gnuplotpipe, "exit\n");
    pclose(gnuplotpipe);
    return 0;
}


#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
using namespace std;
int main(){
    FILE* gnuplotpipe;
    string command;
    gnuplotpipe = popen("gnuplot -persist","w");
    if(!gnuplotpipe)
        cerr<<("there are no gnuplot");

command =
    "set terminal jpeg size 3400,2300 font 'Times New Roman-bold,48';"
    "set output 'graphic.jpg';"

    "set pm3d map;"
    "set pm3d at b;"
    "set pm3d interpolate 0,1;"
    "set pm3d corners2color c1;"
    "set palette defined (0 \"blue\", 10 \"cyan\", 30 \"green\", 45 \"yellow\", 70 \"orange\", 100 \"red\");"
    "set cbrange [0:1];"
    "set ticslevel 0;"
    "splot \"matrixData.dat\" matrix;";

    fprintf(gnuplotpipe,"%s",command.c_str());
    fflush(gnuplotpipe);


    fprintf(gnuplotpipe, "exit\n");
    pclose(gnuplotpipe);
    return 0;
}



понедельник, 16 октября 2017 г.

Язык Swift: функции


  • Функция - это самодостаточный кусок кода (слова ни о чем, но с чего то ж надо начинать);
  • Функция может быть быть без параметров и/или без возвращаемого типа;
  • Функция может возвращать несколько значений (пользуемся для этого кортежами);
  • У параметров функции есть два имени: внутреннее (локальное) имя и внешнее имя (в Swift3 оно стало называться меткой аргумента). Внутреннее имя доступно только внутри тела функции, метка аргумента может использоваться при вызове функции. Метки аргумента могут быть полезны с точки зрения читаемости кода. Метки аргументов являются частью имени функции и их наличие обязательно при вызове функции. При отсутствии меток аргумента используются локальные имена. При наличии меток аргумента - их надо обязательно использовать во время вызова функции. Если хочется писать вызов функции без метки аргумента в вызове, используйте знак подчеркивания _;
  • Метка аргумента у параметра есть всегда. По умолчанию она равна локальному имени; 
  • Параметры функции могут иметь значения по умолчанию. Эти параметры размещаются в конце списка параметров;
  • Функция может иметь переменное число параметров. Для этого используется так называемый variadic (варируемый?) параметр. Внутри функции такой параметр рассматривается как массив соответствующего типа. Список параметров функции может иметь только один варируемый параметр и он должен размещаться в самом конце этого списка и при наличии параметров со значением по умолчанию - после них.
  • Функция может изменять внешние относительно ее переменные не только через возвращаемое значение, но через список параметров. Для этого используется модификатор inout перед типом параметра при определении функции и амперсанд & перед аргументом функции при ее вызове;
  • Каждая функция имеет свой тип, который состоит из типов параметров функции и типа, возвращаемого ею. Этот тип можно использовать как и любой другой тип; Это можно использовать при передаче функции как параметра в другую функцию и/или при возвращении функции как результат работы этой другой функции;
  • Функции, в соответствии с их областью видимости, могут быть глобальными и вложенными.  Глобальные видимы во внешней области видимости. Вложенные видимы только внутри тела другой функции, где они определены, хотя они могут быть переданы "наружу" как возвращаемые объекты.
  • Замыкание в Swift - это самодостаточный блок кода, который может быть рассматриваться изолированно от других частей кода. Замыкание аналогично блоку в языках Си и объектный Си или лямбда-выражению.
  • Функции, как вложенные так и глобальные могут расматриваться как частный случай замыкания. Глобальные функции - это замыкания, которые имеют имя и не "захватывают" переменные, вложенные функции - это замыкания, которые имеют имя и могут "захватывать" переменные, находящиеся в охватывающей функции, а замыкания вообще - это функции без имени (анонимные функции, лямбда-функции), которые могут "захватывать" переменные из окружающего контекста. Что такое "захватывать" нигде явно не говорится. Такое себе интуитивное понятие.
Продемонстрируем сказанное выше кодом для playground:
 import Cocoa

func myFunc(){
    print("Hello!")
}
myFunc()

func myFunc1 () -> Void{
    print("Hello1!")
}
myFunc1()

func myFunc2() -> String{
    return "Hello2!"
}
print(myFunc2())

func myFunc3(numberOgGreatings: Int, textOfGreating: String) -> Void{
    for _ in 1...numberOgGreatings{
        print("\(textOfGreating)")
    }
}
myFunc3(numberOgGreatings: 3, textOfGreating: "Hello3!")

func theFunc() -> Void{
    print("theNothing")
}

func myFunc4(funcParameter: ()->Void){
    funcParameter()
}
myFunc4(funcParameter: theFunc)

func myFunc5()->(param1:String, param2:Int){
    let myInt: Int = 1
    let myString: String = "one"
    return (myString, myInt)
}
let returnedTuple = myFunc5()
print("string=\(returnedTuple.param1)\tinteger=\(returnedTuple.param1)")

func myFunc6(externalName1 localName1: Int, externalName2 localName2: String){
    print("\(localName1)\t\(localName2)")
}
myFunc6(externalName1: 2, externalName2: "two")

func myFunc7(labelArgument1 paramName1: Int, _ paramName2: String)->Void{
    print("\(paramName1)\t\(paramName2)")
}
myFunc7(labelArgument1:3,"three")

func myFunc8(_ myNumber:Int = 1, _ myString:String = "default hello") -> Void{
    print("\(myNumber)\t\(myString)")
}
myFunc8()

func myFunc7(_ mySum :Double , myArrayParam :Double...) -> Double{
    var sum:Double = 0;
    for term in myArrayParam{
        sum += term
    }
    return sum + mySum
}

print(myFunc7(1.1, myArrayParam: 1.2, 3.4, 5.6))

func mySwap( _ forSwap1: inout Int, _ forSwap2: inout Int) -> Void{
    let temp:Int=forSwap1
    forSwap1 = forSwap2
    forSwap2 = temp
}

var a:Int = 1
var b:Int = 2
print("\(a)\t\(b)")
mySwap(&a, &b)
print("\(a)\t\(b)")

func myFunc8(myVar1: Int, myVar2: String) -> String{
    if myVar1 < 0 || myVar2.isEmpty{
            return "False"
        }else{
            return "True"
    }
}

var myFunc9: (Int, String)->String = myFunc8
print(myFunc9(1, "0"))

func myFunc10(funcPar: (Int, String)->String)->Void{
    print(funcPar(1, "str"))
}

var myArr: [Int] = [14, 15, 16]
func myFunc11(summator: (Int, Int, Int) -> Int,members: [Int])->Int{
    return summator(members[0], members[1], members[2])
}
func mySummator(memb1: Int, memb2: Int, memb3: Int)->Int{
    return memb1 + memb2 + memb3
}
print(myFunc11(summator: mySummator, members: myArr))
Ну и добавлю еще, что, на мое мнение, концепция функции в Swift, достигла своего максимально возможного и полного развития.

суббота, 7 октября 2017 г.

Установка свободного антивируса Clamav на MacOS

Clamav на маке устанавливается через систему портов brew.
Последовательность команд в терминале:
Инсталлируем:
#brew install clamav
Создаем файл конфигурации:
#cd /usr/local/etc/clamav/
#cp freshclam.conf.sample freshclam.conf
Редактируем его:
#sed -ie 's/^Example/#Example/g' freshclam.conf
 Грузим базу сигнатур вирусов
#freshclam -v
Сканируем:
#clamscan -r -i /путь/директории/которую/хотите/проверить
Можете просто корень указать, но это будет долго.
Все

пятница, 9 декабря 2016 г.

Не работает Playground? Восстанавливаем

Часто с моим Xcode бывает следующая ситуация: Playground вроде бы и работает, но не показывает результаты. В этом случае мне помогает вот такая последовательность действий:

  • Выходим из Xcode.app и Simulator.app;
  • В терминале: 
    $ rm -fr ~/Library/Developer/*
    $ sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService
    $ rm -rf ~/Library/*/CoreSimulator
  • ЗапускаемXcode (и Simulator.app);
Ну, что еще добавить? Будьте осторожны с командой rm. 

пятница, 25 ноября 2016 г.

Язык Swift: массивы и словари


Рассмотрим коротко массив:
  • Массив - это упорядоченный список значений (величин, элементов);
  • Каждая величина в массиве позиционируется целым числом или индексом. Первая величина в списке имеет индекс 0;
Проиллюстрируем это куском из Playground:
var str = "Hello, playground"
var myArray1: Array<String> = ["myString1","myString2"]
var myArray2: [String] = ["myString3", "myString4"]
let myArray3: [String] = ["myString5", "myString6"]
let myArray4 = ["myString7","myString8"]
var myArray5 = [String]()
var myArray6 = Array<String>()
var myArray7: Array<AnyObject>
Перечислим типичные действия, что мы можем применить к массивам:
  • С помощью свойства count определяется число элементов в массиве;
  • Получение значения элемента массива с помощью индекса;
  • Добавление нового элемента к массиву с помощью функции append(_) или оператора +=;
  • Удаление выбранного элемента с помощью функции remove(at:);
  • Добавление нового элемента в массив в указанную позицию помощью функции insert(_,at:);
  • Изменение значения выбранного элемента с помощью оператора =;
  • Просмотр содержимого массива с помощью цикла for-in;
Проиллюстрируем это куском из Playground:
print("number elements in the array: \(myArray1.count)")
print("\(myArray1[1])")
myArray1.append("myString9")
myArray1+=["myString10"]
myArray1.remove(at: 0)
myArray1.insert("myString11", at: 1)
myArray1[0]="myString12"
for i in myArray1{
    print("\(i)")
}
Аналогично рассмотрим словарь:
  • Словарь - это неупорядоченный список абстрактных пар ключ-значение (ключ-величина, ключ-элемент);
  • Как в массиве элемент определяется по индексом, величиной целого типа, так и в словаре элемент определяется ключом, величиной любого типа. Я думаю, что можно говорить об массиве как частном случае словаря;
  • На практике в большинстве случаев тип ключа - это String;

Проиллюстрируем это куском из Playground:
var myDictionary1: Dictionary<String, String> = [:]
var myDictionary2: Dictionary<String, String> = ["myKey1":"myValue1","myKey2":"myValue2"]
var myDictionary3: [String: String] = ["myKey3":"myValue3","myKey4":"myValue4"]
var myDictionary4 = [String:String]()
var myDictionary5 = Dictionary<String, String>()

Перечислим типичные действия, что мы можем применить к словарям:
  • С помощью свойства count определяется число пар в словаре;
  • Получение значения элемента с помощью ключа;
  • Изменение значения элемента с данным ключом с помощью функции updateValue(_,forKey:) или оператора =;
  • Добавление новой пары ключ-значение;
  • Удаление пары пары ключ-значение с помощью функции removeValue(forKey:) либо присваиванием удаляемому элементу значения nil;
  • Просмотр содержимого словаря с помощью цикла for-in;
Проиллюстрируем это куском из Playground:
print("number elements in the dictionary:\(myDictionary2.count)")
print("\(myDictionary2["myKey2"]!)")
myDictionary2.updateValue("newMyValue2", forKey: "myKey2")
print("\(myDictionary2["myKey2"]!)")
myDictionary2["myKey2"] = "new2MyValue2"
print("\(myDictionary2["myKey2"]!)")
myDictionary2["myKey5"] = "myValue5"
myDictionary2.removeValue(forKey: "myKey1")
let arrayOfMyKeys = [String] (myDictionary2.keys)
let arrayOfMyValues = [String] (myDictionary2.values)
for i in arrayOfMyKeys{
    print("\(myDictionary2[i]!)")
}


четверг, 13 октября 2016 г.

Минимальный набор команд OpenGL для работы с вершинным шейдером

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

Glint programID = 0;
GLuint vaoID = 0;
char[] source = "the code1";//коды шейдеров
char[] source = "the code2";
Glint logLength = 0;
GLint compiled = 0;
GLint linked = 0;

Список команд в инициализирующей части:

programID = glCreateProgram();
glGenVertexArrays(1, &vaoID);
vs = glCreateShader(GLenum(GL_VERTEX_SHADER));
glShaderSource(vs, 1, &source, nil);
glCompileShader(vs);

//Работа с логами и кодами возврата компиляции шейдера
glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled);
glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength);
glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog);
free(cLog);

fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER));
glShaderSource(fs, 1, &fssptr, nil);
glCompileShader(fs);
//Здесь надо повторить обработку кода возврата компиляции и лог

glAttachShader(programID, vs);
glAttachShader(programID, fs);
glLinkProgram(programID);

//Работа с логами и кодами возврата линковки шейдера
glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked);
glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength);
glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog);

glDeleteShader(vs);
glDeleteShader(fs);

Список команд в рисующей части:
glClear(GLbitfield(GL_COLOR_BUFFER_BIT));
glUseProgram(programID);
glBindVertexArray(vaoID);

//glPointSize(40); и т.п.

glDrawArrays(GLenum(GL_POINTS), 0, 1);
        
glBindVertexArray(0);       
glFlush();

По завершении работы необходимо освободить ресурсы:

glDeleteVertexArrays(1, &vaoID);
glDeleteProgram(programID);

Это некая абстрактная схема, выраженная в виде инструкций на языке Си. На "человеческом" языке этот алгоритм можно описать так:

  1. Создаем пустую программу с идентификатором programID;
  2. Создаем массив вершин;
  3. Задаем исходники шейдера вершин;
  4. Компилируем их;
  5. Смотрим, есть ли ошибки компиляции, и обрабатываем их;
  6. Повторяем шаги 2-5 для других массивов;
  7. Привязываем все скомпиллированные шейдеры к программе programID;
  8. Компонуем ее;
  9. Смотрим, есть ли ошибки компоновки, и обрабатываем их;
  10. Вытираем шейдеры; Все готово для команд рисования;
  11. Указываем, что будем пользоваться программой с индентификатором programID; Я так понял, что таких "программ" может быть несколько в одном приложении и мы можем переключаться между ними;
  12. "Привязываем" массив вершин;
  13. glDrawArrays - рисуем;
  14. "Отвязываем" массив вершин;
  15. При выходе из приложения явно вытираем массивы вершин и "программы";