Управляем чем угодно откуда угодно

Образ со сборкой ferro-remote

Обсуждение клиентского ПО для доступа к виртурилке, управления и мониторинга

Re: Образ со сборкой ferro-remote

Сообщение nwnclv » 27 окт 2016, 23:40

Пропал ненадолго. Тут опишу про вызов system (1 вызов всего) и файловую систему (уже посложнее).
вам подстать написать на хабре.

У меня плохо с написанием =)

========================

И снова здравствуйте.

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

И так. Маленький компонент FrClientOS, в который я изначально хотел собрать всякие вызовы работы с операционной системой, но получился пока только один вызов. Вызов execute принимает в качестве параметра строку и исполняет на стороне агента шелл с этой командой. Короче делает банальный вызов system со всеми его плюсами и минусами.
Плюс в том, что он довольно простой для реализации и использует шелл юзера под которым будет исполнен (в случае виртурилки это рутовый bash)
Минус в том, что он довольно долгий и если агент занят исполнением этого вызова, то этот вызов занимает один из потоков-исполнителей. Это значит, что если эти потоки исполнители будут заняты все, то клиент уже не сможет ничего исполнить, не дождавшись завершения system, Поэтому нужно аккуратно эту штуку использовать.

Компонент QML
Как писал выше, называется он FrClientOS и требует только основного для всех компонентов свойства - client, которым он связан с соединенным клиентом. Выглядит все это очень просто:
Код: Выделить всё
    Item {
        FrClientOS {
            id: shellExecutor /// id по которому будет идентифицирован внутри скрипта
            client: remoteClient
        }
    }

Item на самом деле не обязателен, но, по традиции из прошлых постов, сделаю его тоже отдельным айтемом.
Далее план просто: Сделать поле ввода, сделать кнопку и по нажатию этой кнопки сделать shellExecutor.execute(cmd).
Получилось что-то типа такого:
Код: Выделить всё
    Item {
        FrClientOS {
            id: shellExecutor
            client: remoteClient
        }
        Connections { /// ракция на нажатие кнопки runButton
            target: runButton
            onClicked: shellExecutor.execute( command.text )
        }
    }

    Column {
        anchors.centerIn: parentRect
        spacing: 10

        TextField {
            id: command
            height: 20
            width:  parentRect.width - 10
            text: "echo `uname -a`"
        }

        Button {
            id: runButton
            width:  command.width
            text: "Remote run"
        }
    }

Полный код скрипта лежит тут
окошко выглядит вот так
Изображение

С этим все просто.
Следующая часть - Файловая система.
Эта подсистема агента довольно большая и состоит из 3 основных частей:

1: Основа. В ее задаче содержать путь, по которому будет происходить работа остальных частей (это, грубо говоря то, что показывает PWD). Так же предоставляет свединия об объектах файловой системы: тип (диреткория/файл/устройство, время создания/доступа, etc)
От основы создаются/открываются файлы (вторая часть) и итераторы директории (3 часть)

2: Файл. Предоставляет доступ к файлу. Можно читать, писать, передвигать позицию чтения/записи, ограниченно можно сделать ioctl для открытого файла/устройства.

3: Итераторы директорий. Тут все просто. Открываем (или, лучше сказать, получаем) итератор и пробегаем им до конца директории, получая сведения об объектах.

Исполнение скрипта:

Для рассказа о том, как работать с FS сделаю простой примерчик.
Примерчик будет:
Писать некий скрипт в /tmp устройства
Ставить ему флажок исполнения (chmod +x)
Запускать его

В первом пункте мне поможет компонент FrClientFs, в остальных двух FrClientOS из описания выше.
Я просто изменю предыдущий пример, добавив в него FrClientFs и заменив TextField на TextArea:
Код: Выделить всё

    Item {

        property string filePath: "/tmp/fsexample.sh" // так будет называться наш файл
        id: shellExec

        FrClientFs {
            id: remoteFs
            client: remoteClient
            path: "/"
        }

        FrClientOS {
            id: executor
            client: remoteClient
        }
        Connections {
            target: runButton
            onClicked: shellExecutor.execute( command.text )
        }
    }

    Column {
        anchors.centerIn: parentRect
        spacing: 10

        TextArea {
            id: command
            height: parentRect.height - 40
            width:  parentRect.width - 10
            text: "#!/bin/bash"
        }

        Button {
            id: runButton
            width:  command.width
            text: "Remote run"
        }
    }

Теперь на обработчике кнопки можно сделать 3 действия. Записать файл, сделать chmod, выполнить.
Оформлю это в отдельную функцию, потому как дальше сделаю еще кой чего.

Код: Выделить всё
        function runShell( )
        {
            remoteFs.writeFile( filePath, command.text )
            executor.execute( "chmod +x " + filePath )
            executor.execute( filePath )
        }

функция writeFile из FrClientFs может записать в файл до 40килобайт. По-умолчанию пишет с 0 смещения, но смещение можно задать 2 параметром.
Далее 2 уже знакомых execute, который выполнят нужные команды.

Я немного переделал пример и теперь он будет спрашивать, нужно ли переписать файл, или удалить его, если таковой найден. Что получилось, см во вложении. Сам пример ТУТ пример небольшой и несложный. Думаю понятно, что там происходит.
===
Тут еще функции, которые есть у
FrClientFs:
exists( str ) - Проверяет найден ли файловый объект
mkdir( str ) - создает директорию
remove( str ) - удаляет файл или пустую директорию
removeAll( str ) - удаляет все рекурсивно
info( str ) - получить некоторую информацию про объект файловой системы:
Возвращает объект, который содержит свойства: exists - если объект найден, directory - если объект директория, empty - если объект пустая директория или файл, regular - если это обычный файл/директория, symlink - если это ссылка.
readFile( path, max, from = 0 ) - читает max байт (но не больше 40к) из файла по пути path, со смещения from
writeFile( path, data, from = 0 ) - пишет data (но не больше 40к) в файл по пути path, со смещения from. Возвращает количество записанных байт.

:!: В протоколе еще предусмотрел вызов stat, который является проводником вызова stat, но почему-то в QML клиента я его не вывернул. Видимо за ненадобностью.
Вложения
shell-exec3.png
shell-exec2.png
shell-exec.png
shell-exec.png (10.86 КБ) Просмотров: 3143
shell-run.png
shell-run.png (6.63 КБ) Просмотров: 3143
nwnclv
 
Сообщения: 67
Зарегистрирован: 22 авг 2014, 19:04

FR. Fs Client, File Client

Сообщение nwnclv » 05 ноя 2016, 02:06

И снова здравствуйте.

Тут я опишу часть подсистемы ФС, которую выше обозначил как третью. То есть доступ к файлу. Это открытие, чтение, запись, перемещение позиции и тд.

С прошлого поста доделал клиент. Добавил вызов openFile в FrClientFs.

К сути:

Клиент, который открывает и работает с файлами называется FrClientFile. Как и все остальные объекты, должен содержать свойство client, которым будет связан с определенным агентом. Далее 2 свойства path и mode, которые отвечают за путь к файлу и способ его открытия. mode это строка, которая принимает значения, описанные в fopen в разделе mode.
Код: Выделить всё
FrClientFile {
    id: tmpFile
    client: remoteClient
    path: "/tmp/tmp.txt"
    mode: "rw"
}

Тут описан объект, который соединен с удаленным агентом через remoteClient, связан с файлом "/tmp/tmp.txt", который будет открыт для чтения и записи (mode: "rw").
Файл пока не открыт. Открывается он вызовом open( ). Закрывается вызовом close( );
Еще одно свойство у FrClientFile - position, которое указывает на текущую позицию файла. С нее будет происходить дальнейшее чтение или запись. Это свойство меняется автоматически при чтении/записи. Может быть изменено "вручную".

Объект FrClientFile может быть получен не только путем декларативного описания, но и может быть получен динамически вызовом openFile из объекта
FrClientFs.

Нарисую небольшой пример, который будет читать файл с текстовое поле и записывает его обратно после редактирования. В аттаче скриншот того, что полилось.
Собственно из всего файла интересны только 2 момента. Это чтение и запись файла. Файл будет открываться динамически.
Чтение; это реакция на нажатие кнопки openFile:
Код: Выделить всё
            Connections {
                target: openFile
                onClicked: {
                    var f = remoteFs.openFile( filePath.text, "r" ) // открываем для чтения
                    while( true ) {
                        var t = f.read( 4096 ); // читаем по 4к байт
                        if( t.toString().length === 0 ) { // если мы достигнут конец файла, то можно выйти
                            break;
                        }
                        testField.text += t; // иначе записываем прочитанный кусок в поле
                    }
                    f.close( ) // закрыть файл. На самом деле не обязательно.
                }
            }

f.close( ) Добавил просто для примера. На самом деле файл будет закрыт как только будет удален объект.

С записью все так же просто.
Код: Выделить всё
            Connections {
                target: saveButton // реакция на нажатие другой кнопки
                onClicked: {
                    var f = remoteFs.openFile( filePath.text, "w" ) // откроем файл для записи
                    var begin = 0; /// позиция строки, с которой нужно начинать запись
                    while( true ) {
                        var res = f.write( testField.text.slice( begin ) ); // write вернет количество байт, которое ей удалось записать за 1 вызов.
                        if( res === 0 ) break; // если записано 0 байт, то begin, указывает на конец текста, выходим
                        begin += res // увеличиваем begin
                    }
                    f.close( )
                }
            }

Собственно работа с файлами не отличается от работы с файлами локально.
Весь пример полностью можно взять тут
Ну и видео. Качество, правда отвратное. Оказывается не так просто набирать что-то на клавиатуре, когда на ней тренога стоит :D
https://www.youtube.com/watch?v=Y5PfUDlcw94
Вложения
fs-sample02.png
fs-sample02.png (8.8 КБ) Просмотров: 3121
nwnclv
 
Сообщения: 67
Зарегистрирован: 22 авг 2014, 19:04

FR. File Client

Сообщение nwnclv » 12 ноя 2016, 23:11

Тут я расскажу зачем нужен компонент FrClientFile (описан в прошлом сообщении) как декларативный клиент.

Понятно, что описанный декларативно объект живет все время "существования" скрипта. Это может быть файл конфига, который периодически нужно открывать и читать, это может быть специальное устройство, с которого нужно снимать данные.

Вот с устройства я, пожалуй, и начну. Но для начала нужно рассказать еще об одной возможности объекта, описанного как FrClientFile.
Это события (да, я люблю события). Ранее я описывал как ловить события от клиентов GPIO и делал некий "компас", вращаемый енкодером, а так же работу с дальномером.

Открытый файл тоже может быть подписан на события. Правда эти события поддерживают не все типы файлов. События поддерживаются файлами устройств. Одно из таких устройств - /dev/random.

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

Для примера я возьму предыдущий пример и изменю его. Нужно:
Назначить файлу путь
Подписаться на события
Открыть файл
Далее просто подписаться на события объекта (! это уже события компонента) и выводить все данные файла в компонент TextArea.
В общем выглядит все вот так
Изображение
По нажатии на кнопку открывается файл, происходит подписка на события и потом (если файл, а точнее устройство, может поддерживать события) все данные выводятся в окно текста.

Основное изменение это:
Компонент:
Код: Выделить всё
        FrClientFile {
            id: remoteFile
            client: remoteClient
        }

Описан некий файл, пока без имени и он не открыт.
По нажатию кнопки установка пути файла, подписка на события на стороне агента, и открытие.
Код: Выделить всё
        Connections {
            target: openFile
            onClicked: {
                remoteFile.close( ) // закрыть файл, если он открыт
                remoteFile.path = filePath.text // назначить новый путь
                remoteFile.events = true // сказать агенту, что хотим событий от устройства
                remoteFile.open( ) // открыть
            }
        }

Теперь, если устройство поддерживает события, данные с файла будут выводиться в окно. Сама подписка на события со сторону компонентов QML:
Код: Выделить всё
            Connections {
                target: remoteFile
                onFileEvent: {
                    testField.text = testField.text + data
                }
            }

Теперь можно видеть, как через некоторые время в окне появляется новая порция данных из /dev/random
==================
Можно ловить события от мыши или клавиатуры. Так как у меня нет мыши на виртурилки я запустил агента на рабочем ноуте и подписался на /dev/input/mouse1, то есть мышку (mouse0 у меня тачпад). В результате получил, то, что показывает картинка в аттаче.

Так же можно подписаться на клавиатуру, консоль, COM порт и многие другие устройства.

Файл примера по традиции можно найти ТУТ
Вложения
fs-example.04.png
fs-example.03.png
fs-example.03.png (26.83 КБ) Просмотров: 3096
nwnclv
 
Сообщения: 67
Зарегистрирован: 22 авг 2014, 19:04

Пред.

Вернуться в Софт на управляющих устройствах (iOS, Android, Win/Mac/Linux)

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron