3 объекта под окном - stdin, stdout, TCPSocket

Пока не хочу создавать задачу на Bitbucket - решил обсудить здесь:

В чем суть

  1. посыл номер “раз” - в стандартных скриптовых движках (особенно под *nix) доступны считывания КонсольногоВывода и КонсольногоВвода. В С# это по моему System.out и System.in потоки.

  2. посыл номер “два” - в скриптах мы сейчас часто используем запуск команд системы, для автоматизации сборок и развертывания, часто так сказать несколько “извращаясь” как-раз с КонсольнымВыводом запущенной команды.

  3. посыл номер “три” - уже сейчас есть задача на HTTP клиентское соединение, и в ее необходимости вроде как никто не сомневается.

  4. посыл номер “четыре” - есть стойкое ощущение, что если повторять функционал 1С платформы в некотором виде, то обязательно в скором времени кто-нибудь попросит HTTPСерверныйСервис.

И вот тут - собственно что я хочу обсудить:

  1. как базис для HTTP сервиса, придется использовать “слушающий” socket.
  2. запуская команду системы (в том числе и под Mono) нужно будет сохранять контекст вызова

В итоге мне видится, что подобные объекты внутри движка скорее всего будут несколько асинхронными, точнее работать по событию OnReceive. Придется видимо еще и “таймауты” использовать.

@EvilBeaver @pumbaE - я в общем думаю, что это целый комплект задач, а не одна маленькая задача. Но решать что-то нужно - хотелось бы услышать Ваши мысли.

Речь идет в том числе о следующих вещах:

и

Леш, а что именно ты хочешь делать с чтением консольного ввода? Я бы еще понял подключение к потокам другого процесса, который мы запускаем и хотим как-то читать выхлоп этого процесса. Что предполагается делать со stdin/stdout процесса oscript.exe?

Ну и про асинхронность и таймауты - вообще не понял. Не мог бы ты развернуть?

Я именно это и имел ввиду - только видимо сформулировал не верно, то есть я бы хотел чтобы при работе с процессами запускаемыми из скрипта, мы подцеплялись к консольному выводу и вводу как к своему. Чтобы можно было выполнить команды типа такой:

echo -e "ИмяПользователя\nПароль" | oscript updater_v8_platform.os

Что касается асинхронности и таймаутов, то тут я вижу работающим следующий код

Сервер = Новый TCPСервер(5431);
Пока Истина Цикл
    Сервер.ПерейтиВПриемСоединения(); // здесь исполнение кода тормозится и в Idle режиме сервер ждет соединений  
    Сервер.ПрочитатьДанные(); //Сюда исполнение кода приходит, в случае если получены данные, но парралельно с этим сервер должен возвращать новым соединениям статус ожидания.
КонецЦикла 

Конечно можно добавить асинхронности и реализовать работающим следующим код

Процедура ПриПолученииДанных(Данные)
  Сообщить(Данные);
КонецПроцедуры

Сервер = Новый TCPСервер(5431);
Сервер.ПерейтиВПриемСоединения("ПриПолученииДанных"); 

Но тогда мы должны уметь запускать сервер в режиме “демона” или сервиса.

P.S. Опять как-то сумбурно получилось.

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

Что-то вроде:

Пока Сервер.ЖдатьСоединения(5431) Цикл
    Соединение = Сервер.ПолучитьСоединие();
    Соединение.ПоставитьВОчередьОбработки("ПриПолученииДанныхСоединения");
КонецЦикла;

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

Создал задачу по сокету https://bitbucket.org/EvilBeaver/1script/issue/102/socket
поигрался с BDD заодно

Запилил на коленке сокет и слил в develop.
Пример использования можно посмотреть в файле теста socket.os. Написано исключительно тупо и в лоб, поэтому просьба погонять на реальном функционале.

Начал гонять… :wink: Завтра к утру будут результаты.

Пришлось немного поизвращаться с процессами

то есть основной скрипт - запускает Wire сервер тестирования и потом уже сам cucumber
в 2-ух потоках с одним лог файлом

#!/usr/bin/env ruby

pid = Process.spawn "oscript.exe cuke4onec-server.os"
p "1Script Tests Server run with pid #{pid} "

IO.popen("cucumber #{ARGV.join(" ")}", "r+") do |pipe|
  p pipe.gets
end

Process.kill('INT', pid)

UPDATE: А вот отправить строку не получается в сокет.

Следующий код

Сервер = Новый TCPСервер(54321);
Сервер.Запустить();
Сообщить("Жду соединения");
Пока Истина Цикл
		
	Соединение = Сервер.ОжидатьСоединения();
	
	Сообщить("Есть соединение");
	
	Данные = Соединение.ПрочитатьСтроку("utf-8");
	
	Сообщить(Данные);
	
	Соединение.ОтправитьСтроку("['success',[]]", "utf-8");
	
	Соединение.Закрыть();
	
КонецЦикла;


Сервер.Остановить();

В логах дает следующую ошибку

"1Script Tests Server run with pid 648 "

Жду соединения

Есть соединение
["begin_scenario"]

{Модуль cuke4onec-server.os / Ошибка в строке: 15 / Внешнее исключение}
  	Соединение.ОтправитьСтроку("['success',[]]", "utf-8");
Операция не разрешается на неподключенных сокетах.

Кажется я понял свою ошибку :slight_smile: Попробуй сейчас (когда jenkins соберет очередную версию).

Сервер = Новый TCPСервер(54321);

Сервер.Запустить();

Пока Истина Цикл

	Сообщить("Жду соединения");
	
	Соединение = Сервер.ОжидатьСоединения();
	
	Сообщить("Есть соединение");
	
	Данные = Соединение.ПрочитатьСтроку("utf-8");
	
	Сообщить(Данные);
	
	Сообщить("Отправляю данные в ответ");
	Соединение.ОтправитьСтроку("['success',[]]", "utf-8");
	
КонецЦикла;


Соединение.Закрыть();
Сервер.Остановить();

Вот такой код приводит к зависанию вызывающего процесса, такое впечатление что Отправка данных как-то НЕ передает управление вызывающему.

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

Насколько я вижу из картинки - данные отправлены. Он написал “Отправляю данные”, а затем “Жду соединения”. Т.е. метод “ОтправитьСтроку” отработал и вернул управление, после чего вновь поток ожидает нового соединения. Или я чего-то недопонял? Висит метод ОтправитьСтроку или что?

@EvilBeaver Андрей, раз уж речь зашла про асинхронность, спрошу. Нет ли планов добавить в OneScript отслеживание изменений файлов? Что-то типа win-api’шного FindFirstChangeNotification. Для автоматизации собственной деятельности было-бы очень удобно запускать скрипт автоматом при изменении файла.

А скрипт, который мониторит изменения постоянно висит в процессах, так что-ли? И когда дожидается - запускает другой скрипт? Или как?

Висит в процессах, да. А запускать-ли другой скрипт - это зависит от реализации. Можно и в этом скрипте обработать изменение файла и снова уйти в ожидание. Идеально, конечно, если бы была возможность задавать процедуру-обработчик события. Чтобы можно установить слежку за несколькими файлами или директориями. Если же делать какой-то объект с методом ОжидатьИзменения(), то хотелось бы иметь возможность задавать список файлов/директорий, в которых ожидаются изменения. Но с таким методом, кмк, будут риски пропустить изменения, если изменится файл в тот момент, пока обрабатывается изменение другого файла.

Если говорить о планах, то таких планов не было. Но если говорить о самой задаче, по-моему уже есть решения по мониторингу файлов и запуску чего-либо. Можно погуглить.

В 7-ой Java добавили такое кроссплатформенное API - http://docs.oracle.com/javase/tutorial/essential/io/notification.html
Там как раз есть заметки по конкурентности и потокобезопасности

На ruby есть специальный gem для этого https://github.com/guard/guard/wiki/Guardfile-examples
Паразитриует на libevent, libnotify и spawn|fork потоках

Кстати @EvilBeaver - вот этот код забирает stdout потока с запущенным cucumber в текущий stdout

IO.popen("cucumber #{ARGV.join(" ")}", "r+") do |pipe|
  p pipe.gets
end

аналог подобного я и имел ввиду когда говорил про stdout

Блин, ruby взрывает мне мозг. Я бы лучше понял, если бы вместо двух строк на руби ты привел десять, но на C:) Это я так, слову. Задача-то понятна, просто код на руби содержит слишком много магии для тех, кто его не знает:P

Найти бы еще, эти 10 строчек на С :wink: - я так понимаю магия Ruby в использовании NamedPipes

Интересующий нас код выглядит примерно так

process.StartInfo.UseShellExecute = true;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();

Насколько я обнаружил - делается такое в NET|Mono через OutputDataReceived

В нашем случае код будет выглядеть как-то так

Сообщить("Сейчас вы увидите прямо тут STDOUT от 1С ");
УправлениеПроцессами.ЗапуститьДочернийКомандныйПроцесс("d:\onec\1cv8.exe /DESIGNER /FD:\IbService");
Сообщить("Увидели ?");