среда, 31 мая 2017 г.

Offtopic. Казбек. Трубка

Почему-то ещё вспомнилось. Мы как-то ходили на Казбек. Со стороны Майли и Колки.

Весной. В тот год когда потом лавина сошла и Бодров погиб. И многие другие.

Погода была жуткая.

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

Незабываемое ощущение.

А потом нас накрыла непогода и на спуске мы не смогли дойти до лагеря и словили холодную сидячую ночёвку. Рыли в снегу ямы и ночевали в них. Укрываясь плащами.

А на следующий день опять была непогода.

И мы рыли снежную пещеру в наддуве.

Но это - другая история.

Возможно это всё - "цена выкуреной трубки". ;)

Но после таких "встрясок" - почему-то жизнь сразу начинает играть новыми яркими красками.

Ни о чём

Продолжим "вечер брюзжания".

Многое хочется в блоге написать про программирование.

Прям вот реально чувствую, что в последнее время - "пересматриваю свои ошибки молодости".

И вроде вижу пути - "как их не делать".

Хочется вот прям взять и "рубануть правду-матку" и покритиковать себя "молодого".

Никак только с духом не соберусь.

Себя кстати критиковать - гораздо интереснее, чем других.

Это - реально "вкусно". Разнести себя в пух и прах. Зная всю подноготную.

И поизмываться вволю. Над другими так не поиздеваешься.

Я правда как-то уже пытался.

Воспринималось публикой как - "ну что же вы нам очевидные вещи рассказываете".

Ну попробую найти "изюминку".

#1356. Как я делаю бекапы. СУБД FireBird

14010197737483.png
Беда пришла откуда не ждали…
У клиента завис процесс “Касса”, так что не смог снять процесс через Диспетчер задач.
Рабочее место “Касса” - одновременно сервер всей системы.

Клиент принял решение ресетнуть через кнопку.

В итоге умерла DB. FireBird 2.5

Backup’ы настроены не были, так что последняя версия БД, которая случайно лежала у меня на винте, была минус 8 дней. Подняли по-быстрому с неё. Но суть дальше.

Как делать бекапы для FireBird.
  • Я написал скрипт, который, когда его запускают, делает резервное копирование базы данных с именем БазаДаннах_30_05_2017_23_07_51.fbk

@echo off
set "currentTime=%Time: =0%"
set now=%date:~-4%_%date:~3,2%_%date:~0,2%_%currentTime:~0,2%_%currentTime:~3,2%_%currentTime:~6,2%
  
set user=SYSDBA
set password=masterkey
set database_name=PARKDB.FDB
set backup_name=Backup\PARKDB
set ext=.fbk

set backup_filename=%backup_name%_%now%%ext%
echo %backup_filename%

nbackup -U %user% -P %password% -B 0 %database_name% %backup_filename%

%date:~3,2%

Это означает взять из результата date(Это системная функция Windows), 2 символа, начиная с 3 позиции в строке
Обычный %date%  = 31.05.2017

set "currentTime=%Time: =0%"

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

set now=%date:~-4%_%date:~3,2%_%date:~0,2%_%currentTime:~0,2%_%currentTime:~3,2%_%currentTime:~6,2%

получили бы, что now=2017_05_31_ 0_44_33 ,

а имя файла выглядело бы так: Backup\PARKDB_2017_05_31_ 0_44_33.fbk

Пробел не сильно виден, но он неприемлем в названии файла для nbackup

  • дата и время берутся из текущего времени системы

Это вроде рассказал :).

  • далее, чтобы скрипт выполнялся постоянно,
  • я добавил задачу в планировщик задач виндовс,
  • он каждые 4 часа запускает мой скрипт
  • и создается новая БД,
  • далее я научился создавать задачи в планировщике задач через командную строку

@echo off
set script_name=e:\SoftBuild\Parking\DB\DB_Backup.bat
set task_name=LotParkingBackup
SCHTASKS /Create /SC DAILY /TN %task_name% /TR %script_name% /HRESULT /F /RI 240 /DU 24:00 /v1

Детали параметров можете посмотреть в хелпе. SCHTASKS /Create /?

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

function NextButtonClick(CurPageID: Integer): Boolean;
var
  ServerHost, ServerPort, DBFileName, FBDirPath: string;
  ResultCode, ErrorCode: Integer;
  UDFFrom, UDFTo, ReaderPort: string;
  RegistryTaskFile, DBDirPath, BackupScriptPath, RegistryFileName: string;
begin
  if CurPageID = SettingsPage.ID then
  begin
    ServerHost := SettingsPage.Values[0];
    ServerPort := SettingsPage.Values[1];
    DBFileName := SettingsPage.Values[2];

    if IsComponentSelected(cDB) then
    begin
      DBDirPath := Copy(DBFileName, 1, Pos('PARKDB.FDB', DBFileName) - 1);
      BackupScriptPath := DBDirPath + 'DB_Backup.bat'
      RegistryTaskFile := '@echo off' + #13#10 + 
                          'set script_name=' + BackupScriptPath + #13#10 + 
                          'set task_name=LotParkingBackup' + #13#10 + 
                          'SCHTASKS /Create /SC DAILY /TN %task_name% /TR %script_name% /HRESULT /F /RI 240 /DU 24:00 /v1' + #13#10;
      
      RegistryFileName := DBDirPath + 'DB_RegistryBackup.bat';
      SaveStringToFile(RegistryFileName, RegistryTaskFile, False);
      
      Exec(ExpandConstant(RegistryFileName), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
    end;    
  end;
end;
  • теперь у меня есть скрипт, который делает резервную копию,
  • и скрипт, который регистрирует задачу,
  • далее я научился удалять задачу из планировщика,
  • опять же из командной строки

@echo off
set task_name=LotParkingBackup
SCHTASKS /DELETE /TN %task_name% /F

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

[UninstallRun]
Filename: "{app}\DB\DB_DeleteTask.bat"; WorkingDir: "{app}\DB\"; Flags: runhidden waituntilterminated; Components: DB

В итоге у нас каждые 4 часа есть бекап, и если мы делаем UnInstall, то всё чисто :)



#1355. Получаем AV при создании интерфейсов

type
 ISomeInterface = interface
  function Ptr: Pointer;
 end;//ISomeInterface 

 TSomeInterface = class(TInterfacedObject, ISomeInterface)
  f_Ptr: Pointer;
  function Ptr: Pointer;
  constructor Create(aPtr: Pointer; aSize: Integer);
  destructor Destroy; override;
 end;//TSomeInterface 

  function TSomeInterface.Ptr: Pointer;
  begin
   Result := f_Ptr;
  end;

  constructor TSomeInterface.Create(aPtr: Pointer; aSize: Integer);
  begin
   inherited Create;
   GetMem(f_Ptr, aSize);
   Move(f_Ptr^, aPtr^, aSize);
  end;

  destructor TSomeInterface.Destroy;
  begin
   FreeMem(f_Ptr);
   f_Ptr := nil;
   inherited;
  end;

const
 C : array [0..2] of Char = 'ABC';

var
 A : ISomeInterface;
 P : Pointer;

A := TSomeInterface.Create(@C, SizeOf(C));
P := A.Ptr;
A := TSomeInterface.Create(@C, SizeOf(C));
A := TSomeInterface.Create(P, SizeOf(C)); // - AV !!! Потому, что предыдущий A уже освобождён


четверг, 25 мая 2017 г.

Размышления

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

Если функционал хорошо покрыт тестами, то главное это, чтобы все тесты проходили. Пусть код "костылен" и неидеален.

Если тесты проходят, то это придаёт "уверенность в завтрашнем дне".

И при этом это позволяет убирать "костыльность" и неидеальность.

Но постепенно. А не "сразу".

среда, 24 мая 2017 г.

Offtopic. Любителям истории

http://periskop.livejournal.com/766659.html

"С фотоаппаратом по линии Зигфрида".

У меня есть скан этого номера Техники Молодёжи.

Люди упорно ищут

Люди упорно ищут контекст - "тест-кейс для проверки калькулятора".

И приходят ко мне в блог.

Что же реально они хотят найти?

Не понимаю 2...

Или написать DoFoo и потом комментарий - "этот метод делает то-то и то-то".

А по-человечески метод нельзя назвать?

Не понимаю...

Написать UpdateSomeState(2).

И далее написать комментарий:

// 2 - это "такая то константа", она делает то-то и то-то

Const или Enum уже отменили?

И ведь так - ПОВСЕМЕСТНО.

вторник, 23 мая 2017 г.

Смешно. "Смешит" и Embarcadero и Штефан Глинке

https://plus.google.com/u/0/+StefanGlienke/posts/Ys1NnBKQQxU?cfem=1
It just occured to me that when I have such code:

type
TFooBar = class
s: string;
end;

procedure Main;
var
x, y: TFooBar;
begin
x := TFooBar.Create;
y := TFooBar.Create;

x.s := 'True';
y.s := 'True';

Assert(Pointer(x.s) = Pointer(y.s));
end;

begin
Main;
end.

The assertion will raise because whenever you assign a string literal to a string variable it checks if the string is a const (refcount = -1) and then calls _NewUnicodeString). I guess this has been discussed already somewhere else but I cannot find anything regarding my question:

I would guess that even if it is a const it could assign that reference to s and don't touch the refcount. This should work fine with the cow mechanics.

But as it is this would mean that if code runs through such const string assignments every time a new string gets allocated, no?
In my case I am using the const string 'True' for nullables in Spring to set the "HasValue" flag for them (otherwise the field is empty). However by using a const there it creates a new string every time. So if you have 10 nullable values you have 10 string instances with the content "True". The only solution I found so far was to make the const a variable (in this case it works because its private) and assign 'True' to it in the initialization section (or could also in the class ctor). That way there is only one string instance with content 'True' for the nullables around.

Any other solution I am missing?

Edit: I found this SO question:
https://stackoverflow.com/questions/12837129/string-const-why-different-implementation-for-local-and-result so I changed the original example to use objects and non global variables.

In my case I know that the module the literal is coming from will not be unloaded before anyone that is using it. That makes me think if we would need some kind of const string with start refcount = 1. Since its a const you could not modify it (which would modify the const because of refcount 1) but when assigning somewhere the same reference is being used and no new strings are being produced. I guess I am missing some cornercases why this is not possible -.- So I probably will use the hidden string variable to get the same result.

вторник, 16 мая 2017 г.

Offtopic. Сказка закончилась №2

ВТОРОЙ МОМЕНТ.

Про СКАЗКУ и КРАСОТУ.

Когда-то в аэропортах с парковкой и организацией движения была СКАЗКА и КРАСОТА. Почти ЕВРОПА.

Но и это всё изгавняли. Простите.

Вот тут был в Шереметьево на машине. Заплатил 700 руб за "парковку" в течении 22 мин.

В чём же дело?

А очень просто.

Я подъехал, чтобы забрать пассажира. Взял билет. Поехал к выходу. Над одним выходом написано "VIP. Международные линии".

Думаю - НЕ ТУДА.

Еду дальше. Дальше написано - "внутренние линии".

ВСЁ - БОЛЬШЕ НИКАКОЙ информации нет.

СПРОСИТЬ - не у кого.

Подъехал. Спросил какого-то мужика - он говорит "не это внутренние линии, где международные - не знаю".

Выехал. Поехал на второй круг.

Опять взял билет - поехал туда где написано - "VIP. Международные линии".

Долго ездил по "кишке" с поворотами. Упёрся в шлагбаум. На нём написано "приложите билет или вставьте карту". Билет прикладывать - НЕКУДА.

Матерясь выехал по "кишке задним ходом".

Подъехал обратно к "внутренним линиям". Поставил машину. Нашёл пассажира. Посадил.

Оказалось, что всех выпускают там где написано "внутренние линии". И с международных - ТОЖЕ.

По часам вроде - в 15 мин уложился.

Подъезжаю к шлагбауму. Вставляю билет. А мне пишут - "требуется оплата". Платить негде.

За мной выстроился хвост из 5 машин.

Я всех "растолкал" и выехал задом к паркоматам.

У паркоматов стоит человек десть и все пытаются оплатить. НИ У ОДНОГО не получается. Все засовывают тысячные купюры - всем их возвращают. Кто-то ругается по громкой связи с парковщиками.

Току - НОЛЬ.

Минут через пять дождался своей очереди.

У меня были мелкие купюры. ПО 100 руб.

О ЧУДО! У меня паркомат оплату ПРИНЯЛ.

И у следующих за мной - ТОЖЕ.

ПРОБЛЕМА БЫЛА в ОТСУТСТВИИ сдачи.

ПАРКОВЩИКИ об этом - НИ УХОМ НИ РЫЛОМ.

ПО чеку - я потратил 22 мин. Т.е. с учётом времени оплаты - я не уложился видимо где-то в 1 (ОДНУ) минуту.

Всё изгавняли.

РЫБЯТЫ!

Мне НЕ 700 руб ЖАЛКО! Мне "за ДЕРЖАВУ обидно"!

Была КРАСОТА, СКАЗКА и ЕВРОПА.

Стало...

Ну стало - "как обычно"...

ОЧЕНЬ ЖАЛЬ.

Ну и указатетели и знаки в Шереметьево - ГОВНО. Извините...

Offtopic. "Сказка закончилась"...

"Да никак "они" не планируют   Они плитку около метро по третьему разу перекладывают   В "этом мире" давно уже важен "процесс", а не "результат". Я давно и всё время об этом говорю."

У нас почему-то даже если что-то работало Perfekt! - То и то перестаёт работать.

Вот смотрите.

ЗАМЕЧАТЕЛЬНАЯ вещь - экспрессы в аэропорт.

Когда они только появились - это была СКАЗКА! Это была ЕВРОПА! Это был ЦИВИЛИЗОВАННЫЙ МИР!

Билет стоил 100 руб. Билет можно было купить прямо в экспрессе. Если у тебя был билет на самолёт, то можно было ехать БЕСПЛАТНО.

СКАЗКА! ЕВРОПА!

Не говоря уж о том, что на том же Павелецком вокзале можно было накануне вечером зарегистрироваться и сдать багаж. И ехать на следующий день налегке. Уже зарегистрированным и БЕЗ БАГАЖА.

СКАЗКА! ЕВРОПА!

Сейчас - НИЧЕГО ЭТОГО НЕТ. :-(

Ну и экспресс стоит не 100 руб, а 700. Могу путать точные цифры.

И главное - всё "скатилось в жопу". Простите мой "французский".

Недавний пример.

Приехали мы тут на Белорусский вокзал. С Кунцево. На путях стоит поезд-экспресс. Пустой. С открытыми дверями. Куча людей в форме РЖД.

Билетов у нас нет. Надо покупать билеты. Для этого надо выйти с платформы к кассам. И купить билет.

Подхожу к служащему - спрашиваю - "а можно оплатить билет прямо в поезде или как-то ещё". Он пожимает плечами и говорит - "я не знаю".

Один. Другой. Третий.

Ну вышли. Купили билеты.

Оказалось конечно, что можно было купить билеты например через приложение. Или оплатить картой PayPass на выходе из поезда.

НИКТО НИЧЕГО НЕ ЗНАЕТ!

СКАЗКА и ЕВРОПА - ЗАКОНЧИЛИСЬ :-(

Начались обыденные серые будни и "синром вахтёра".

Про синдром.

Купили мы билеты. Идём обратно к экспрессу.

Там - ВСЁ КРАСИВО - перрон, навес. КРАСОТА!

НО!

На перрон - НЕ ПУСКАЮТ.

Собрали всех людей в апендиксе между кассами и перроном. Отгородили шлагбаумом. Идёт дождь. Люди стоят, мокнут. А рядом, через шлагбаум - КРАСОТА и навес от дождя. Но! ТУДА НЕ ПУСКАЮТ.

Пускать стали ТОЛЬКО за 5 мин ДО ОТПРАВЛЕНИЯ экспресса.

Всё! СКАЗКА и ЕВРОПА - ЗАКОНЧИЛИСЬ!

А когда-то было ЗДОРОВО и КРАСИВО.

РЫБЯТЫ! А САМ - "патриот", но НЕЛЬЗЯ ЖЕ ТАК! Нельзя вот так взять и ВСЁ ИЗГОВНЯТЬ. ТО ЧТО УЖЕ РАБОТАЛО!

Извините.