Перейти к содержанию

Евгений Корепов

Пользователи
  • Активность

    720
  • Зарегистрирован

  • Посещение

  • Победитель дней

    91

Весь контент Евгений Корепов

  1. Как верно подметил Дмитрий - знание SQL наше все. Вот к примеру я создал тестовую таблицу и наполнил ее тестовыми данными: CREATE TABLE `test001` ( `id` int(11) NOT NULL AUTO_INCREMENT, `TimeFrom` datetime DEFAULT NULL, `InA` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8; INSERT INTO `test001` VALUES ('1', '2020-05-17 16:14:36', '1'); INSERT INTO `test001` VALUES ('2', '2020-05-17 16:12:56', '2'); INSERT INTO `test001` VALUES ('3', '2020-05-17 16:12:56', '3'); INSERT INTO `test001` VALUES ('4', '2020-05-17 17:12:56', '4'); INSERT INTO `test001` VALUES ('5', '2020-05-17 17:12:56', '5'); INSERT INTO `test001` VALUES ('6', '2020-05-17 17:12:56', '6'); INSERT INTO `test001` VALUES ('7', '2020-05-17 18:12:56', '7'); INSERT INTO `test001` VALUES ('8', '2020-05-17 18:12:56', '8'); INSERT INTO `test001` VALUES ('9', '2020-05-17 18:12:56', '9'); INSERT INTO `test001` VALUES ('10', '2020-05-17 19:12:56', '10'); INSERT INTO `test001` VALUES ('11', '2020-05-17 19:12:56', '11'); INSERT INTO `test001` VALUES ('12', '2020-05-17 19:12:56', '12'); INSERT INTO `test001` VALUES ('13', '2020-05-17 20:12:56', '13'); INSERT INTO `test001` VALUES ('14', '2020-05-17 20:12:56', '14'); INSERT INTO `test001` VALUES ('15', '2020-05-17 20:12:56', '15'); INSERT INTO `test001` VALUES ('16', '2020-05-17 21:12:56', '16'); INSERT INTO `test001` VALUES ('17', '2020-05-17 21:12:56', '17'); INSERT INTO `test001` VALUES ('18', '2020-05-17 21:12:56', '18'); Для получения почасовых сумм по полю InA мне достаточно очень простого запроса: SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600), SUM(test001.InA) as SumInA FROM test001 GROUP BY FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600 Результат запроса будет выглядеть вот так: 2020-05-17 16:00:00 6 2020-05-17 17:00:00 15 2020-05-17 18:00:00 24 2020-05-17 19:00:00 33 2020-05-17 20:00:00 42 2020-05-17 21:00:00 51 Скорость выдачи результата будет большой - при миллионах записей в исходной таблице дело нескольких секунд или десятков секунд (зависит от железа сервера). Но это все равно медленно, потому что на каждую запись таблицы будет производится вычисление "FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600" - деление, округление и умножение. Если это разовая выборка - проблем нет. Но если вам нужно регулярно дергать из таблицы данные, то лучше добавить в таблицу поле TimeFromHour, в которое сразу записывать значение FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600. Это можно сделать руками или триггером при вставке. Добавить Индекс по полю TimeFromHour и в запросе тоже группировать (GROUP BY) по этому полю: ALTER TABLE `test001` ADD INDEX `Index-TimeFromHour` (`TimeFromHour`) USING BTREE ; Тогда вы получите максимально возможное быстродействие. Если нужно результат вставить в другую таблицу, то используйте запрос вроде такого: INSERT INTO test_sum (TimeFrom, SumInA) ( SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600), SUM(test001.InA) as SumInA FROM test001 GROUP BY FLOOR(UNIX_TIMESTAMP(test001.TimeFrom) / 3600) * 3600 )
  2. По умолчанию в TThread классовая функция GetTickCount имеет тип Cardinal, соответственно если приложение рассчитано на долговременную работу, то через 40 дней результат этой функции или уложит приложение (если отключена защита от переполнения) или превратит приложение в тыкву (если на этом завязана какая то логика). Для своего проекта написал хелпер для класса TThread, вдруг кому пригодится. Тестил на win10 x64 и linux x64. unit UnitTThreadHelper; interface uses {$IFDEF MSWINDOWS} Winapi.Windows, {$ENDIF MSWINDOWS} {$IFDEF POSIX} Posix.SysTypes, {$IFDEF MACOS} Macapi.CoreServices, {$ENDIF MACOS} {$IFDEF ANDROID} Posix.Fcntl, {$ENDIF ANDROID} {$IFDEF LINUX} Posix.Time, {$ENDIF LINUX} {$ENDIF POSIX} System.Classes; type TThreadHelper = class helper for TThread class function GetTickCount64 : Int64; static; end; implementation class function TThreadHelper.GetTickCount64 : Int64; {$IF Defined(MSWINDOWS)} begin Result := Winapi.Windows.GetTickCount64; end; {$ELSEIF Defined(MACOS)} begin Result := AbsoluteToNanoseconds(mach_absolute_time) div 1000000; end; {$ELSEIF Defined(POSIX)} var res: timespec; begin clock_gettime(CLOCK_MONOTONIC, @res); Result := (Int64(1000000000) * res.tv_sec + res.tv_nsec) div 1000000; end; {$ELSE OTHERPLATFORM} {$MESSAGE Fatal 'Method not implemented for Platform'} {$ENDIF OTHERPLATFORM} end. При попытке запуска на win x32 упадет - не используйте или проверяйте битность: if CheckWin32Version(6, 0) then begin GetTickCount64; end else begin GetTickCount; end;
  3. Класс для работы с FastCGI серверами, такими как php-fpm. Для тех кто не в теме : веб-сервер, к примеру nginx, получает запросы и отправляет их на исполнение FastCGI серверу, к примеру php-fpm, php-fpm получает имя скрипта, параметры, выполняет все и возвращает результат в nginx, который в свою очередь возвращает этот результат запросившему клиенту. С помощью класса вы можете так же, выполнять php скрипты на локальном или удаленном сервере. Исходники https://github.com/EvgeniyKorepov/FastCGIClient Пример delphi var FFastCGI : TFastCGI; ... procedure TFormMain.FormCreate(Sender: TObject); var AHost : String; APort : Word; AScriptFileName, ARequest, AContent : String; begin AHost := '10.0.0.4'; APort := 9000; FFastCGI := TFastCGI.Create(AHost, APort); FFastCGI.KeepAlive := True; AScriptFileName := '/opt/xxx.php'; ARequest := 'request=1234567890'; if FFastCGI.Get(AScriptFileName, ARequest, AContent) then Memo.Text := AContent else Memo.Text := FFastCGI.StatusCode.ToString + ' ' + FFastCGI.StatusText; end; пример php <?php header('Content-Type: text/html; charset=utf-8'); if (isset($_REQUEST["request"])) if ($_REQUEST["request"] == "1234567890") { header("Status: 200"); echo 'OK'; } else { http_response_code(400); echo "ERROR"; }
  4. В прошлой теме я показал как делать демонов старой школы - серьезных и самодостаточных. Но это все в прошлом. Теперь существуют системы управления процессами, где все намного, намного проще. Исходники тут https://github.com/EvgeniyKorepov/LinuxDaemonNewStyle Заметьте, теперь никаких потоков, все элементарно, systemd сделает всю работу за нас. Половина кода - разбор параметров командной строки. Вторая половина - бесконечный цикл, ожидающий сигналов от экземпляра класса демона нового стиля . Код самого класса сократился на треть. Если запустите приложения в консоли - будет самое обычное приложение, которые умрет при закрытии консоли. Запуск в качестве бессмертного демона - через systemd, причем он сможет автоматически перезапускать вашего демона при крахе. Эти параметры задаются в текстовом файле /etc/systemd/system/DaemonNewStyleTest.service program DaemonNewStyleTest; {$APPTYPE CONSOLE} uses System.SysUtils, System.IOUtils, System.SyncObjs, Posix.Stdlib, Posix.SysStat, Posix.SysTypes, Posix.Unistd, Posix.Signal, Posix.Fcntl, Posix.Syslog in 'Posix.Syslog.pas', UnitDaemonNewStyle in 'UnitDaemonNewStyle.pas'; var AEvent : TEventType; begin if ParamCount = 0 then begin syslog(LOG_ERR, 'No parameters'); ExitCode := EXIT_FAILURE; exit; end; if ParamStr(1).ToLower.Equals('stop') then begin if Daemon.Stop(30) then ExitCode := EXIT_SUCCESS else ExitCode := EXIT_FAILURE; exit; end; if ParamStr(1).ToLower.Equals('reload') then begin if Daemon.Reload() then ExitCode := EXIT_SUCCESS else ExitCode := EXIT_FAILURE; exit; end; if not ParamStr(1).ToLower.Equals('start') then begin syslog(LOG_ERR, 'Unknow parameters'); ExitCode := EXIT_FAILURE; exit; end; syslog(LOG_NOTICE, 'main START'); while Daemon.IsRunning do begin syslog(LOG_NOTICE, 'main LOOP'); Daemon.Execute(AEvent); if AEvent <> TEventType.None then syslog(LOG_NOTICE, 'main Daemon receive signal'); case AEvent of TEventType.Start : begin syslog(LOG_NOTICE, 'main Event START'); end; TEventType.Reload : begin // Reload config syslog(LOG_NOTICE, 'main Event RELOAD'); end; TEventType.Stop : begin syslog(LOG_NOTICE, 'main Event STOP'); ExitCode := EXIT_SUCCESS; Sleep(10); // simulate destroy delay break; end; end; Sleep(1000); end; end. systemctl start DaemonNewStyleTest.service systemctl reload DaemonNewStyleTest.service systemctl stop DaemonNewStyleTest.service
  5. У меня в досягаемости только CentOS 7, ну еще Дебиан есть на паре старых серваков. Под ними все работает превосходно. Тут надо под отладкой запускать и смотреть что происходит. Ну и логи системы смотреть
  6. Сначала пишем серверную часть - загрузка сохранения файла на любимом языке. Потом тестируем серверную часть - бросаем на сервер страничку с формой загрузки. Убеждаемся что все работает в браузере. И только после этого пишем клиента...
  7. Вам проще адаптировать сайт https://gector-spb.ru/ для мобильных браузеров. Наймите верстальшика за доширак - обойдется дешевле и по времени гораздо быстрее.
  8. Я написал класс для демонизации приложения в linux. Класс форкает процесс, обрабатывает поступающие сигналы и передает их в основной поток через потокобезопасную очередь. Исходники https://github.com/EvgeniyKorepov/LinuxDaemon Для использования просто подключите модуль UnitDaemon в свое консольное приложение: program DaemonTest; {$APPTYPE CONSOLE} uses System.SysUtils, System.IOUtils, System.SyncObjs, Posix.Stdlib, Posix.SysStat, Posix.SysTypes, Posix.Unistd, Posix.Signal, Posix.Fcntl, Posix.Syslog in 'Posix.Syslog.pas', UnitDaemon in 'UnitDaemon.pas'; var AEventType : TEventType; begin syslog(LOG_NOTICE, 'main START'); while True do begin syslog(LOG_NOTICE, 'main LOOP'); if UnitDaemon.QueueEvent.PopItem(AEventType) = System.SyncObjs.TWaitResult.wrSignaled then begin syslog(LOG_NOTICE, 'main UnitDaemon.QueueEvent.PopItem'); case AEventType of TEventType.StopProcess : begin syslog(LOG_NOTICE, 'main Event StopProcess'); ExitCode := EXIT_SUCCESS; exit; end; TEventType.Start : begin syslog(LOG_NOTICE, 'main Event START'); end; TEventType.Reload : begin // Reload config syslog(LOG_NOTICE, 'main Event RELOAD'); end; TEventType.Stop : begin syslog(LOG_NOTICE, 'main Event STOP'); ExitCode := EXIT_SUCCESS; exit; end; end; end; Sleep(50); end; end. Так же поддерживается systemd - для этого положите DaemonTest.service в /etc/systemd/system/ и используйте : systemctl start DaemonTest.service systemctl reload DaemonTest.service systemctl stop DaemonTest.service
  9. Вот просто идеальная статья по запуску демона под Linux http://blog.paolorossi.net/2017/09/04/building-a-real-linux-daemon-with-delphi-part-2/ Помимо объяснения механизма fork с отличными примерами, есть куча дополнительной наиполезнейшей инфы.
  10. И еще вариант похожий, источник https://stackoverflow.com/questions/44415054/execute-external-program-from-linux-delphi-10-2-console-application uses ... System.SysUtils, Posix.Base, Posix.Fcntl, ...; type TStreamHandle = pointer; function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen'; function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose'; function fgets(buffer: pointer; size: int32; Stream: TStreamHandle): pointer; cdecl; external libc name _PU + 'fgets'; function runCommand(const acommand: MarshaledAString): String; // run a linux shell command and return output // Adapted from http://chapmanworld.com/2017/04/06/calling-linux-commands-from-delphi/ var handle: TStreamHandle; data: array [0 .. 511] of uint8; function bufferToString(buffer: pointer; maxSize: uint32): string; var cursor: ^uint8; endOfBuffer: nativeuint; begin if not assigned(buffer) then exit; cursor := buffer; endOfBuffer := nativeuint(cursor) + maxSize; while (nativeuint(cursor) < endOfBuffer) and (cursor^ <> 0) do begin result := result + chr(cursor^); cursor := pointer(succ(nativeuint(cursor))); end; end; begin result := ''; handle := popen(acommand, 'r'); try while fgets(@data[0], sizeof(data), handle) <> nil do begin result := result + bufferToString(@data[0], sizeof(data)); end; finally pclose(handle); end; end; function createQRCode(id, fn: string): string; // Create qr-code using qrencode package begin deletefile(fn); if fileExists(fn) then raise Exception.create('Old file not deleted!'); // I am targeting rhel for now, so I know the path for sure result := runCommand(MarshaledAString(UTF8STring('/usr/bin/qrencode -o ' + fn + ' ''' + id + ''''))); if not fileExists(fn) then raise Exception.create('New file not created!'); end; function testqr: String; // Test QR Code creation with error handling // QREncode does not output anything but who knows ;-) begin try result := createQRCode('08154711', '/tmp/myqrcode.png'); except on e: Exception do begin result := 'Error: ' + e.message; end; end; end;
  11. Отсюда https://chapmanworld.com/2017/04/06/calling-linux-commands-from-delphi/ program myls; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Posix.Base, Posix.Fcntl; type TStreamHandle = pointer; /// <summary> /// Man Page: http://man7.org/linux/man-pages/man3/popen.3.html /// </summary> function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen'; /// <summary> /// Man Page: http://man7.org/linux/man-pages/man3/pclose.3p.html /// </summary> function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose'; /// <summary> /// Man Page: http://man7.org/linux/man-pages/man3/fgets.3p.html /// </summary> function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets'; /// <summary> /// Utility function to return a buffer of ASCII-Z data as a string. /// </summary> function BufferToString( Buffer: pointer; MaxSize: uint32 ): string; var cursor: ^uint8; EndOfBuffer: nativeuint; begin Result := ''; if not assigned(Buffer) then begin exit; end; cursor := Buffer; EndOfBuffer := NativeUint(cursor) + MaxSize; while (NativeUint(cursor)<EndOfBuffer) and (cursor^<>0) do begin Result := Result + chr(cursor^); cursor := pointer( succ(NativeUInt(cursor)) ); end; end; var Handle: TStreamHandle; Data: array[0..511] of uint8; begin try Handle := popen('/bin/ls -lart','r'); try while fgets(@data[0],Sizeof(Data),Handle)<>nil do begin Write(BufferToString(@Data[0],sizeof(Data))); end; finally pclose(Handle); end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
  12. А вот я и не прав! Теперь я тоже проклят и напоролся на точно такую же проблему. Действительно, PopItem в непредсказуемый момент изменяет поведение - любой таймаут (от 1 до INFINITE) превращается в 0. И похоже проблеме уже 2 года, но Эмбаркадера делает вид что в " я в домике". Т.е. полностью игнорирует проблему. Дело похоже как обычно в злощастном TMonitor - то одна проблема, то другая, то опять внедряют предыдущую проблему. Вот найденные ссылки по этой проблеме: https://forums.embarcadero.com/thread.jspa?messageID=941762 https://quality.embarcadero.com/browse/RSP-19993 А вот тут речь тоже о той же самой проблеме, дата 2012 год https://delphihaven.wordpress.com/2012/01/30/fixing-tthreadedqueue-or-in-other-words-tmonitor-again/
  13. Вам нужно провести чистый эксперимент на простом приложении и простых условиях. И не использовать Double для условий - лучше использовать целочисленные переменные, чтоб быть уверенным в отсутствии накопления ошибки. Еще важный момент - создание очереди TThreadedQueue<TBytes>.Create(100, 1, 1) не совсем корректно. Вы ставите одинаковый и минимально возможный таймаут на pop и push, и рискуете словить взаимную блокировку. Пока идет push невозможен pop, и наоборот. Обычно, в зависимости от условий, ставят одно гораздо больше другого, к примеру если важна целостность подаваемых данных, то push 1000, а если нужен быстрый поток (который параллельно делает что то еще), то pop 10. Или если поток только обрабатывает входящие данные, то pop 3600 * 24, чтоб он зря не крутился без данных ))) Сейчас у меня приложение на Delphi 10.3.3 крутится месяц без остановки, архитектура - Основной поток -> Поток получения данных -> Поток обработки данных -> 100-200 потоков исполнителей. Весь обмен на очередях. Плюс поток логов и поток мониторинга (они получают данные, каждый по своей очереди, со всех потоков. И никаких аномалий я не заметил, все работает как часы.
  14. Вы создайте запрос прямо на первоисточнике https://github.com/zxing/zxing/issues - автор вам быстрее поможет. Прошу прощения, не туда ссылку дал ) Вот правильная https://github.com/Spelt/ZXing.Delphi/issues
  15. Поиск в форуме совсем тухлый. Пытался найти "чат" - ни одного результата. Спасибо гуглю, поиск по "fire-monkey чат" показал нужные темы.
  16. Друзья, вы будете смеяться - Эмбаркадера наконец то обратила внимание на заявку по этой проблеме. Правда в стиле моя-твоя непонимай ))) https://quality.embarcadero.com/browse/RSP-16910
  17. В телефоне нет никаких гарантированных аппаратных данных - или по причине китайщины, или по тому что их можно изменить. Если уже вам так хочется "привязаться" к чему то неизменному, то привязывайтесь к телефонному номеру. Хотя и его может не быть. Палец не панацея - у вас нет никаких гарантий что палец принадлежит владельцу персональных данных. 100% дактилоскопических данных пользователя у вас нет, а на этапе "привязки" пальца, телефон уже может быть в руках злоумышленника. У вас не получится сделать идеальную систему с абсолютной системой от дурака. Поэтому я и предложил исходить из того что приложение на телефоне запускает легальных пользователь. Все иное должно описываться в политике конфиденциальности, типа мы чтим закон о ПД, но если вы долбоклюй и отдали телефон и пинкод постороннему, то вы сами пролюбили свою медкарту. И пинкод никак не позволит войти на другом устройстве - пин код сохраняется локально на устройстве, в папке приложения. И если пользователь ввел его верно, то запросы на сервер авторизуются с заранее сохраненного на устройстве токена (guid, куки или что вам больше нравится). Ситуации "А если кто взломает телефон и перенесет какой то файл на другое устройство" вас не должны волновать абсолютно. Вы предоставили пользователю приложение, а задача пользователя - не пролюбить телефон и свои персональные данные. Дополнительно вы прикрываете свою задницу политикой конфиденциальности, где описываете что не обязаны охранять пользователя и его телефон круглые сутки.
  18. Почитал ваши сообщения по этой теме, вот ключевое (с отзывом пользователя) Повторюсь - отпечаток пальца, не то что вам нужно. Он есть у небольшой части устройств. Возьмите на вооружение банковские клиенты - один раз ввел логин/пароль, если успешно, то придумал ПИН-код, и в дальнейшем входишь по нему.
  19. Никакой логики по пальцу не нужно. По пальцу идет разблокировка телефона. Т.е. если пользователь смог запустить приложение - значит это легальный пользователь, потому как он смог разблокировать телефон (пальцем или пин-кодом). Если у вас серьезные требования к безопасности, то возьмите любое банковское приложение и скопируйте с него логику работы. Если требование к безопасности не строгие - то не нужно ничего городить, если пользователь запустил приложение, значит это легитимный пользователь. А если пользователь отдал телефон и свой палец соседу - то он сам дурак ))))
  20. Ну речь не шла о безопасности/авторизации/аутентификация. Речь была о получении уникального идентификатора на устройство. Если нужна авторизация, то ее разумно делать не локально в приложении, а на некоем сервере (а приложение в этом случае выступает тонким клиентом). И как правильно выше отметил Slym - для доступа к файлам приложения вам нужно взломать устройство (получить доступ пользователя root). ССЗБ - аббревиатура в данном контексте означает некие изначально ошибочные решения принятые разработчиком приложения ))) Погуглите ))))
  21. Подозреваю что такая попытка приведет к блокировке учетной записи. Нельзя вмешиваться в показ рекламы или изменять рекламный контент.
  22. В Яндекс-маркете и Амазоне достаточно просто опубликовать приложение.
  23. Вам не нужен TNETHTTPClient, это всего лишь обертка для THTTPClient. Просто сделайте поиск по данному форуму по "THTTPClient" и вам откроется многое.
×
×
  • Создать...