1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

جیسا کہ ہم نے اس موضوع پر غور کیا کہ 1C پروڈکٹس لینکس ماحول میں کیسے کام کرتے ہیں، ایک خرابی دریافت ہوئی - 1C سرورز کے کلسٹر کو منظم کرنے کے لیے ایک آسان گرافیکل ملٹی پلیٹ فارم ٹول کی کمی۔ اور ریک کنسول یوٹیلیٹی کے لیے GUI لکھ کر اس خرابی کو دور کرنے کا فیصلہ کیا گیا۔ Tcl/tk کو ترقی کی زبان کے طور پر منتخب کیا گیا تھا، کیونکہ میری رائے میں، اس کام کے لیے سب سے موزوں ہے۔ اور اس طرح، میں اس مواد میں حل کے کچھ دلچسپ پہلوؤں کو پیش کرنا چاہوں گا۔

کام کرنے کے لیے آپ کو tcl/tk اور 1C تقسیم کی ضرورت ہوگی۔ اور چونکہ میں نے تھرڈ پارٹی پیکجز کا استعمال کیے بغیر بنیادی tcl/tk ڈیلیوری کی زیادہ سے زیادہ صلاحیتوں کو استعمال کرنے کا فیصلہ کیا ہے، اس لیے مجھے ورژن 8.6.7 کی ضرورت ہوگی، جس میں ttk - اضافی گرافک عناصر کے ساتھ ایک پیکیج، جس میں سے ہمیں بنیادی طور پر ttk کی ضرورت ہے۔ ::TreeView، یہ درخت کے ڈھانچے کی شکل میں اور میز (فہرست) کی شکل میں دونوں طرح کے ڈیٹا کو ظاہر کرنے کی اجازت دیتا ہے۔ اس کے علاوہ، نئے ورژن میں، مستثنیات کے ساتھ کام پر دوبارہ کام کیا گیا ہے (ٹرائی کمانڈ، جو بیرونی کمانڈز چلاتے وقت پروجیکٹ میں استعمال ہوتی ہے)۔

پروجیکٹ کئی فائلوں پر مشتمل ہے (حالانکہ کوئی بھی چیز آپ کو ایک میں سب کچھ کرنے سے نہیں روکتی ہے):

rac_gui.cfg - پہلے سے طے شدہ ترتیب
rac_gui.tcl - مین لانچ اسکرپٹ
lib ڈائرکٹری میں ایسی فائلیں ہوتی ہیں جو شروع ہونے پر خود بخود لوڈ ہوجاتی ہیں۔
function.tcl - طریقہ کار کے ساتھ فائل
gui.tcl - مرکزی گرافیکل انٹرفیس
images.tcl - base64 تصویری لائبریری

rac_gui.tcl فائل، درحقیقت، مترجم کو شروع کرتی ہے، متغیرات کو شروع کرتی ہے، ماڈیولز لوڈ کرتی ہے، تشکیلات، وغیرہ۔ تبصرے کے ساتھ فائل کے مشمولات:

rac_gui.tcl

#!/bin/sh
exec wish "$0" -- "$@"

# Устанавливаем текущий каталог
set dir(root) [pwd]
# Устанавливаем рабочий каталог, если его нет то создаём
set dir(work) [file join $env(HOME) .rac_gui]
if {[file exists $dir(work)] == 0 } {
    file mkdir $dir(work)    
}
# каталог с модулями
set dir(lib) "[file join $dir(root) lib]"

# загружаем пользовательский конфиг, если он отсутствует, то копируем дефолтный
if {[file exists [file join $dir(work) rac_gui.cfg]] ==0} {
    file copy [file join [pwd] rac_gui.cfg] [file join $dir(work) rac_gui.cfg]
} 
source [file join $dir(work) rac_gui.cfg]
# Код проверки наличия rac и правильности указания пути в конфиге
# если программа не найдена то будет выведен диалог для указания корректного пути
# и этот путь будет записан в пользовательский конфиг
if {[file exists $rac_cmd] == 0} {
    set rac_cmd [tk_getOpenFile -initialdir $env(HOME) -parent . -title "Укажите путь до rac" -initialfile rac]
    file copy [file join $dir(work) rac_gui.cfg] [file join $dir(work) rac_gui.cfg.bak] 
    set orig_file [open [file join $dir(work) rac_gui.cfg.bak] "r"]
    set file [open [file join $dir(work) rac_gui.cfg] "w"]
    while {[gets $orig_file line] >=0 } {
        if {[string match "set rac_cmd*" $line]} {
            puts $file "set rac_cmd $rac_cmd"
        } else {
            puts $file $line
        }
    }
    close $file
    close $orig_file
    #return "$host:$port"
    file delete [file join $dir(work) 1c_srv.cfg.bak] 
} else {
    puts "Found $rac_cmd"
}

set cluster_user ""
set cluster_pwd ""
set agent_user ""
set agent_pwd ""
## LOAD FILE ##
# Загружаем модули кроме gui.tcl так как его надо загрузить последним
foreach modFile [lsort [glob -nocomplain [file join $dir(lib) *.tcl]]] {
    if {[file tail $modFile] ne "gui.tcl"} {
        source $modFile
        puts "Loaded module $modFile"
    }
}
source [file join $dir(lib) gui.tcl]
source [file join $dir(work) rac_gui.cfg]

# Читаем файл со списком серверов 1С
# и добавляем в дерево
if [file exists [file join $dir(work) 1c_srv.cfg]] {
    set f [open [file join $dir(work) 1c_srv.cfg] "RDONLY"]
    while {[gets $f line] >=0} {
        .frm_tree.tree insert {} end -id "server::$line" -text "$line" -values "$line"
    }    
}

درکار ہر چیز کو ڈاؤن لوڈ کرنے اور ریک یوٹیلیٹی کی موجودگی کی جانچ کرنے کے بعد، گرافیکل ونڈو شروع ہو جائے گی۔ پروگرام انٹرفیس تین عناصر پر مشتمل ہے:

ٹول بار، درخت اور فہرست

میں نے "درخت" کے مواد کو 1C سے ونڈوز کے معیاری آلات سے جتنا ممکن ہو سکے بنایا۔

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

اس ونڈو کو بنانے والا مرکزی کوڈ فائل میں موجود ہے۔
lib/gui.tcl

# установка размера и положения основного окна
# можно установить в переменную topLevelGeometry в конфиг программы
if {[info exists topLevelGeometry]} {
    wm geometry . $topLevelGeometry
} else {
    wm geometry . 1024x768
}
# Заголовок окна
wm title . "1C Rac GUI"
wm iconname . "1C Rac Gui"
# иконка окна (берется из файла lib/imges.tcl)
wm iconphoto . tcl
wm protocol . WM_DELETE_WINDOW Quit
wm overrideredirect . 0
wm positionfrom . user

ttk::style theme use clam

# Панель инсрументов
set frm_tool [frame .frm_tool]
pack $frm_tool -side left -fill y 
ttk::panedwindow .panel -orient horizontal -style TPanedwindow
pack .panel -expand true -fill both
pack propagate .panel false

ttk::button $frm_tool.btn_add  -command Add  -image add_grey_32
ttk::button $frm_tool.btn_del  -command Del -image del_grey_32
ttk::button $frm_tool.btn_edit  -command Edit -image edit_grey_32
ttk::button $frm_tool.btn_quit -command Quit -image quit_grey_32

pack $frm_tool.btn_add $frm_tool.btn_del $frm_tool.btn_edit -side top -padx 5 -pady 5
pack $frm_tool.btn_quit  -side bottom -padx 5 -pady 5

# Дерево с полосами прокрутки
set frm_tree [frame .frm_tree]

ttk::scrollbar $frm_tree.hsb1 -orient horizontal -command [list $frm_tree.tree xview]
ttk::scrollbar $frm_tree.vsb1 -orient vertical -command [list $frm_tree.tree yview]
set tree [ttk::treeview $frm_tree.tree -show tree 
-xscrollcommand [list $frm_tree.hsb1 set] -yscrollcommand [list $frm_tree.vsb1 set]]

grid $tree -row 0 -column 0 -sticky nsew
grid $frm_tree.vsb1 -row 0 -column 1 -sticky nsew
grid $frm_tree.hsb1 -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_tree 0 -weight 1
grid rowconfigure $frm_tree 0 -weight 1

# назначение обработчика нажатия кнопкой мыши
bind $frm_tree.tree <ButtonRelease> "TreePress $frm_tree.tree"

# Список для данных (таблица)
set frm_work [frame .frm_work]
ttk::scrollbar $frm_work.hsb -orient horizontal -command [list $frm_work.tree_work xview]
ttk::scrollbar $frm_work.vsb -orient vertical -command [list $frm_work.tree_work yview]
set tree_work [
    ttk::treeview $frm_work.tree_work 
    -show headings  -columns "par val" -displaycolumns "par val"
    -xscrollcommand [list $frm_work.hsb set] 
    -yscrollcommand [list $frm_work.vsb set]
]
# Установка цветов для чередования в таблице
$tree_work tag configure dark -background $color(dark_table_bg)
$tree_work tag configure light -background $color(light_table_bg)

# Размещение элементов на форме
grid $tree_work -row 0 -column 0 -sticky nsew
grid $frm_work.vsb -row 0 -column 1 -sticky nsew
grid $frm_work.hsb -row 1 -column 0 -sticky nsew
grid columnconfigure $frm_work 0 -weight 1
grid rowconfigure $frm_work 0 -weight 1
pack $frm_tree $frm_work -side left -expand true -fill both

#.panel add $frm_tool -weight 1
.panel add $frm_tree -weight 1 
.panel add $frm_work -weight 1

پروگرام کے ساتھ کام کرنے کا الگورتھم درج ذیل ہے:

1. سب سے پہلے، آپ کو مرکزی کلسٹر سرور (یعنی، کلسٹر مینجمنٹ سرور (لینکس میں، مینجمنٹ "/opt/1C/v8.3/x86_64/ras cluster —daemon") کمانڈ کے ساتھ لانچ کیا جاتا ہے) شامل کرنے کی ضرورت ہے۔

ایسا کرنے کے لیے، "+" بٹن پر کلک کریں اور کھلنے والی ونڈو میں سرور کا پتہ اور پورٹ درج کریں:

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

اس کے بعد، ہمارا سرور درخت میں ظاہر ہوگا اس پر کلک کرنے سے، کلسٹرز کی فہرست کھل جائے گی یا کنکشن کی خرابی ظاہر ہوگی۔

2. کلسٹر کے نام پر کلک کرنے سے اس کے لیے دستیاب فنکشنز کی فہرست کھل جائے گی۔

3۔…

اور اسی طرح، یعنی نیا کلسٹر شامل کرنے کے لیے، فہرست میں موجود کسی ایک کو منتخب کریں اور ٹول بار میں "+" بٹن دبائیں اور نیا ایڈ ڈائیلاگ ظاہر ہو جائے گا:

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

ٹول بار میں بٹن سیاق و سباق کے لحاظ سے کام انجام دیتے ہیں، یعنی اس پر منحصر ہے کہ درخت یا فہرست کا کون سا عنصر منتخب کیا گیا ہے، ایک یا دوسرا طریقہ کار انجام دیا جائے گا۔

آئیے ایڈ بٹن ("+") کی مثال دیکھیں:

بٹن جنریشن کوڈ:

ttk::button $frm_tool.btn_add  -command Add  -image add_grey_32

یہاں ہم دیکھتے ہیں کہ جب بٹن دبایا جائے گا تو "Add" کا طریقہ کار عمل میں آئے گا، اس کا کوڈ:

proc Add {} {
    global active_cluster host
    # Определяем идентификатор выделенного элемента
    set id  [.frm_tree.tree selection] 
    # Определяем значение этого элемента
    set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
    set key [lindex [split $id "::"] 0]
    # в зависимости от того что выделили будет запущена нужная процедура
    if {$key eq "" || $key eq "server"} {
        set host [ Add::server ]
        return
    }
    Add::$key .frm_tree.tree $host $values
}

یہاں tickle کے فوائد میں سے ایک ہے: آپ ایک طریقہ کار کے نام کے طور پر ایک متغیر کی قدر کو پاس کر سکتے ہیں:

Add::$key .frm_tree.tree $host $values

یعنی، مثال کے طور پر، اگر ہم مرکزی سرور کی طرف اشارہ کرتے ہیں اور "+" کو دباتے ہیں، تو Add::server کا طریقہ کار شروع ہو جائے گا، اگر کلسٹر پر - Add::cluster اور اسی طرح (میں اس بارے میں لکھوں گا جہاں ضروری "چابیاں" نیچے سے آتی ہیں)، درج کردہ طریقہ کار سیاق و سباق کے مطابق گرافک عناصر کو کھینچتے ہیں۔

جیسا کہ آپ نے پہلے ہی محسوس کیا ہو گا، فارم سٹائل میں ملتے جلتے ہیں - یہ حیرت انگیز نہیں ہے، کیونکہ وہ ایک طریقہ کار کے ذریعہ ظاہر ہوتے ہیں، زیادہ واضح طور پر فارم کا مرکزی فریم (ونڈو، بٹن، تصویر، لیبل)، طریقہ کار کا نام ٹاپ لیول شامل کریں۔

proc AddToplevel {lbl img {win_name .add}} {
    set cmd "destroy $win_name"
    if [winfo exists $win_name] {destroy $win_name}
    toplevel $win_name
    wm title $win_name $lbl
    wm iconphoto $win_name tcl
    # метка с иконкой
    ttk::label $win_name.lbl -image $img
    # фрейм с полями ввода
    set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
    
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    # фрейм и кнопки
    set frm_btn [frame $win_name.frm_btn -border 0]
    ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }
    ttk::button $frm_btn.btn_cancel -command $cmd -image quit_grey_24 
    grid $win_name.lbl -row 0 -column 0 -sticky nw -padx 5 -pady 10
    grid $frm -row 0 -column 1 -sticky nw -padx 5 -pady 5
    grid $frm_btn -row 1 -column 1 -sticky se -padx 5 -pady 5
    pack  $frm_btn.btn_cancel  -side right
    pack  $frm_btn.btn_ok  -side right -padx 10
    return $frm
}

کال پیرامیٹرز: ٹائٹل، لائبریری سے آئیکن کے لیے تصویر کا نام (lib/images.tcl) اور ایک اختیاری ونڈو نام پیرامیٹر (ڈیفالٹ .add)۔ اس طرح، اگر ہم مین سرور اور کلسٹر کو شامل کرنے کے لیے مندرجہ بالا مثالیں لیں، تو کال اس کے مطابق ہو گی:

AddToplevel "Добавление основного сервера" server_grey_64

یا

AddToplevel "Добавление кластера" cluster_grey_64

ٹھیک ہے، ان مثالوں کو جاری رکھتے ہوئے، میں وہ طریقہ کار دکھاؤں گا جو سرور یا کلسٹر کے لیے ایڈ ڈائیلاگ ظاہر کرتے ہیں۔

شامل کریں::سرور

proc Add::server {} {
    global default
    # выводим основную форму
    set frm [AddToplevel "Добавление основного сервера" server_grey_64]
    # добавляем етки и поля ввода на эту форму
    label $frm.lbl_host -text "Адрес сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
    grid columnconfigure $frm 0 -weight 1
    grid rowconfigure $frm 0 -weight 1
    #set frm_btn [frame .add.frm_btn -border 0]
    # переопределяем обработчик нажатия кнопки
    .add.frm_btn.btn_ok configure -command {
        set host [SaveMainServer [.add.frm.ent_host get] [.add.frm.ent_port get]]
        .frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"
        destroy .add
        return $host
    }
    return $frm
}

شامل کریں::کلسٹر

proc Add::cluster {tree host values} {
    global default lifetime_limit expiration_timeout session_fault_tolerance_level
    global max_memory_size max_memory_time_limit errors_count_threshold security_level
    global load_balancing_mode kill_problem_processes 
    agent_user agent_pwd cluster_user cluster_pwd auth_agent
    if {$agent_user ne "" && $agent_pwd ne ""} {
        set auth_agent "--agent-user=$agent_user --agent-pwd=$agent_pwd"
    } else {
        set auth_agent ""
    }
    # устанавливаем глобальные переменные ()
    set lifetime_limit $default(lifetime_limit)
    set expiration_timeout $default(expiration_timeout)
    set session_fault_tolerance_level $default(session_fault_tolerance_level)
    set max_memory_size $default(max_memory_size)
    set max_memory_time_limit $default(max_memory_time_limit)
    set errors_count_threshold $default(errors_count_threshold)
    set security_level [lindex $default(security_level) 0]
    set load_balancing_mode [lindex $default(load_balancing_mode) 0]
    
    set frm [AddToplevel "Добавление кластера" cluster_grey_64]
    
    label $frm.lbl_host -text "Адрес основного сервера"
    entry  $frm.ent_host
    label $frm.lbl_port -text "Порт"
    entry $frm.ent_port 
    $frm.ent_port  insert end $default(port)
    label $frm.lbl_name -text "Название кластера"
    entry  $frm.ent_name
    label $frm.lbl_secure_connect -text "Защищённое соединение"
    ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
    label $frm.lbl_expiration_timeout -text "Останавливать выключенные процессы через:"
    entry  $frm.ent_expiration_timeout -textvariable expiration_timeout
    label $frm.lbl_session_fault_tolerance_level -text "Уровень отказоустойчивости"
    entry  $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
    label $frm.lbl_load_balancing_mode -text "Режим распределения нагрузки"
    ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode 
    -values $default(load_balancing_mode)
    label $frm.lbl_errors_count_threshold -text "Допустимое отклонение количества ошибок сервера, %"
    entry  $frm.ent_errors_count_threshold -textvariable errors_count_threshold
    label $frm.lbl_processes -text "Рабочие процессы:"
    label $frm.lbl_lifetime_limit -text "Период перезапуска, сек."
    entry  $frm.ent_lifetime_limit -textvariable lifetime_limit
    label $frm.lbl_max_memory_size -text "Допустимый объём памяти, КБ"
    entry  $frm.ent_max_memory_size -textvariable max_memory_size
    label $frm.lbl_max_memory_time_limit -text "Интервал превышения допустимого объёма памяти, сек."
    entry  $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
    label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Принудительно завершать проблемные процессы"
    checkbutton $frm.check_kill_problem_processes -variable kill_problem_processes -onvalue yes -offvalue no    
    
    grid $frm.lbl_host -row 0 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_host -row 0 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_port -row 1 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_port -row 1 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_name -row 2 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_name -row 2 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_secure_connect -row 3 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.cb_security_level -row 3 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_expiration_timeout -row 4 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_expiration_timeout -row 4 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_session_fault_tolerance_level -row 5 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_session_fault_tolerance_level -row 5 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_load_balancing_mode -row 6 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.cb_load_balancing_mode -row 6 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_errors_count_threshold -row 7 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_errors_count_threshold -row 7 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_processes -row 8 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.lbl_lifetime_limit -row 9 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_lifetime_limit -row 9 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_max_memory_size -row 10 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_max_memory_size -row 10 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_max_memory_time_limit -row 11 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.ent_max_memory_time_limit -row 11 -column 1 -sticky nsew -padx 5 -pady 5
    grid $frm.lbl_kill_problem_processes -row 12 -column 0 -sticky nw -padx 5 -pady 5
    grid $frm.check_kill_problem_processes -row 12 -column 1 -sticky nw -padx 5 -pady 5
    # переопределяем обработчик
    .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster insert 
        --host=[.add.frm.ent_host get] 
        --port=[.add.frm.ent_port get] 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth_agent $host"
        Run::server $tree $host ""
        destroy .add
    }
    return $frm
}

ان طریقہ کار کے کوڈ کا موازنہ کرتے وقت، فرق ننگی آنکھ کو نظر آتا ہے؛ میں "اوکے" بٹن ہینڈلر پر توجہ دوں گا۔ Tk میں، آپشن کا استعمال کرتے ہوئے پروگرام کے عمل کے دوران گرافک عناصر کی خصوصیات کو اوور رائڈ کیا جا سکتا ہے۔ ترتیب دیں. مثال کے طور پر، بٹن کو ظاہر کرنے کے لیے ابتدائی کمانڈ:

ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }

لیکن ہماری شکلوں میں، کمانڈ مطلوبہ فعالیت پر منحصر ہے:

  .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster insert 
        --host=[.add.frm.ent_host get] 
        --port=[.add.frm.ent_port get] 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth_agent $host"
        Run::server $tree $host ""
        destroy .add
    }

اوپر کی مثال میں، "کلاگڈ" بٹن کلسٹر کو شامل کرنے کا طریقہ کار شروع کرتا ہے۔

یہاں یہ Tk میں گرافک عناصر کے ساتھ کام کرنے کی طرف توجہ دینے کے قابل ہے - مختلف ڈیٹا ان پٹ عناصر (انٹری، کومبو باکس، چیک بٹن، وغیرہ) کے لیے ایک پیرامیٹر کو ٹیکسٹ متغیر کے طور پر متعارف کرایا گیا ہے:

entry  $frm.ent_lifetime_limit -textvariable lifetime_limit

یہ متغیر عالمی نام کی جگہ میں بیان کیا گیا ہے اور اس میں موجودہ درج کردہ قدر شامل ہے۔ وہ. فیلڈ سے درج کردہ متن کو حاصل کرنے کے لیے، آپ کو صرف متغیر سے متعلق قدر کو پڑھنے کی ضرورت ہے (یقیناً، بشرطیکہ عنصر تخلیق کرتے وقت اس کی تعریف کی گئی ہو)۔

درج کردہ متن کو بازیافت کرنے کا دوسرا طریقہ (اندراج کی قسم کے عناصر کے لئے) get کمانڈ کا استعمال کرنا ہے:

.add.frm.ent_name get

مذکورہ کوڈ میں یہ دونوں طریقے دیکھے جا سکتے ہیں۔

اس بٹن پر کلک کرنے سے، اس صورت میں، ریک کے لحاظ سے کلسٹر کو شامل کرنے کے لیے تیار کردہ کمانڈ لائن کے ساتھ RunCommand کا طریقہ کار شروع ہوتا ہے:

/opt/1C/v8.3/x86_64/rac cluster insert  --host=localhost  --port=1540  --name=dsdsds  --expiration-timeout=0  --lifetime-limit=0  --max-memory-size=0  --max-memory-time-limit=0  --security-level=0  --session-fault-tolerance-level=0  --load-balancing-mode=performance  --errors-count-threshold=0  --kill-problem-processes=no   localhost:1545

اب ہم مرکزی کمانڈ کی طرف آتے ہیں، جو ہمیں درکار پیرامیٹرز کے ساتھ ریک کے آغاز کو کنٹرول کرتا ہے، اگر ضرورت ہو تو کمانڈز کے آؤٹ پٹ کو فہرستوں اور واپسیوں میں پارس بھی کرتا ہے:

رن کمانڈ

proc RunCommand {root par} {
    global dir rac_cmd cluster work_list_row_count agent_user agent_pwd cluster_user cluster_pwd
    puts "$rac_cmd $par"
    set work_list_row_count 0
    # открываем канал в неблокирующем режиме
    # $rac - команда с полным путём
    # $par - сформированные ключи запуска и опции    
    set pipe [open "|$rac_cmd $par" "r"]
    try {
        set lst ""
        set l ""
        # вывод команды добавляем в список списков
        while {[gets $pipe line]>=0} {
            #puts $line
            if {$line eq ""} {
                lappend l $lst
                set lst ""
            } else {
                lappend lst [string trim $line]
            }
        }
        close $pipe
        return $l
    } on error {result options} {
        # Запуск обработчика ошибок
        ErrorParcing $result $options
        return ""
    }
}

مرکزی سرور کا ڈیٹا داخل کرنے کے بعد، اسے درخت میں شامل کر دیا جائے گا، اس کے لیے، مندرجہ بالا Add:server طریقہ کار میں، درج ذیل کوڈ ذمہ دار ہے:

.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"

اب، ٹری میں سرور کے نام پر کلک کرنے سے، ہمیں اس سرور کے زیر انتظام کلسٹروں کی فہرست ملتی ہے، اور ایک کلسٹر پر کلک کرنے سے، ہمیں کلسٹر عناصر (سرور، انفو بیسز، وغیرہ) کی فہرست ملتی ہے۔ یہ TreePress طریقہ کار میں لاگو ہوتا ہے (file lib/function.tcl):

proc TreePress {tree} {
   global host server active_cluster infobase
   # определяем выделенный элемент
    set id  [$tree selection]
   # устанавливаем нужные глобальные переменные
    SetGlobalVarFromTreeItems $tree $id
   # Определяем ключ и значение, т.е. именно тип выбранного элемента
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
   # и в зависимости от того что выбрали будет запущена соответствующая процедура 
   # в пространстве имён Run
    Run::$key $tree $host $values
}

اس کے مطابق، Run::server مرکزی سرور کے لیے شروع کیا جائے گا (ایک کلسٹر کے لیے - Run::cluster، ایک ورکنگ سرور کے لیے - Run::work_server، وغیرہ)۔ وہ. $key متغیر کی قدر درخت کے عنصر کے نام کا حصہ ہے جو آپشن کے ذریعہ بیان کی گئی ہے۔ ایڈ۔.

آئیے طریقہ کار پر توجہ دیں۔

چلائیں::سرور

proc Run::server {tree host values} {
    # получаем список кластеров требуемого сервера
    set lst [RunCommand server::$host "cluster list $host"]
    if {$lst eq ""} {return}
    set l [lindex $lst 0]
    #puts $lst
    # удаляем лишнее из списка
    .frm_work.tree_work delete  [ .frm_work.tree_work children {}]
    # читаем список
    foreach cluster_list $lst {
        # Заполняем список полученными значениями
        InsertItemsWorkList $cluster_list
        # обрабатываем вывод (список) для добавления данных в дерево
        foreach i $cluster_list {
            #puts $i
            set cluster_list [split $i ":"]
            if  {[string trim [lindex $cluster_list 0]] eq "cluster"} {
                set cluster_id [string trim [lindex $cluster_list 1]]
                lappend cluster($cluster_id) $cluster_id
            }
            if  {[string trim [lindex $cluster_list 0]] eq "name"} {
                lappend  cluster($cluster_id) [string trim [lindex $cluster_list 1]]
            }
        }
    }
    # добавляем кластеры в дерево
    foreach x [array names cluster] {
        set id [lindex $cluster($x) 0]
        if { [$tree exists "cluster::$id"] == 0 } {
            $tree insert "server::$host" end -id "cluster::$id" -text "[lindex $cluster($x) 1]" -values "$id"
            # добавляем элементы в кластер
            InsertClusterItems $tree $id
        }
    }
    if { [$tree exists "agent_admins::$id"] == 0 } {
        $tree insert "server::$host" end -id "agent_admins::$id" -text "Администраторы" -values "$id"
        #InsertClusterItems $tree $id
    }
}

یہ طریقہ کار RunCommand کمانڈ کے ذریعے سرور سے موصول ہونے والی چیزوں پر کارروائی کرتا ہے اور درخت میں ہر قسم کی چیزیں شامل کرتا ہے - کلسٹرز، مختلف جڑ عناصر (بیسز، ورکنگ سرورز، سیشنز وغیرہ)۔ اگر آپ قریب سے دیکھیں گے تو آپ کو InsertItemsWorkList طریقہ کار کے اندر ایک کال نظر آئے گی۔ یہ rac کنسول یوٹیلیٹی کے آؤٹ پٹ پر کارروائی کرکے عناصر کو گرافیکل لسٹ میں شامل کرنے کے لیے استعمال کیا جاتا ہے، جو پہلے $lst متغیر کی فہرست کے طور پر واپس کی گئی تھی۔ یہ فہرستوں کی فہرست ہے جس میں بڑی آنت کے ذریعے الگ کیے گئے عناصر کے جوڑے ہوتے ہیں۔

مثال کے طور پر، کلسٹر کنکشن کی فہرست:

svk@svk ~]$ /opt/1C/v8.3/x86_64/rac connection list --cluster=783d2170-56c3-11e8-c586-fc75165efbb2 localhost:1545
connection     : dcf5991c-7d24-11e8-1690-fc75165efbb2
conn-id        : 0
host           : svk.home
process        : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase       : 00000000-0000-0000-0000-000000000000
application    : "JobScheduler"
connected-at   : 2018-07-01T14:49:51
session-number : 0
blocked-by-ls  : 0

connection     : b993293a-7d24-11e8-1690-fc75165efbb2
conn-id        : 0
host           : svk.home
process        : 79de2e16-56c3-11e8-c586-fc75165efbb2
infobase       : 00000000-0000-0000-0000-000000000000
application    : "JobScheduler"
connected-at   : 2018-07-01T14:48:52
session-number : 0
blocked-by-ls  : 0

گرافیکل شکل میں یہ کچھ اس طرح نظر آئے گا:

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

مندرجہ بالا طریقہ کار ٹیبل کو پُر کرنے کے لیے ہیڈر اور ڈیٹا کے لیے عناصر کے نام منتخب کرتا ہے:

InsertItemsWorklist

proc InsertItemsWorkList {lst} {
    global work_list_row_count
    # установка чередования цвета для строки
    if [expr $work_list_row_count % 2] {
        set tag dark
    } else {
        set tag light
    }
    # разбор строк на пары ключ - значение
    foreach i $lst {
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            lappend column_list [string trim $param]
            lappend value_list [string trim $value]
        }
    }
     # заполнение таблицы
    .frm_work.tree_work configure -columns $column_list -displaycolumns $column_list
    .frm_work.tree_work insert {} end  -values $value_list -tags $tag
    .frm_work.tree_work column #0 -stretch
    # установка заголовков
    foreach j $column_list {
        .frm_work.tree_work heading $j -text $j
    }
    incr work_list_row_count
}

یہاں، ایک سادہ کمانڈ [split $str ":"] کے بجائے، جو سٹرنگ کو ":" سے الگ کردہ عناصر میں تقسیم کرتا ہے اور ایک فہرست لوٹاتا ہے، ایک باقاعدہ اظہار استعمال کیا جاتا ہے، کیونکہ کچھ عناصر میں بڑی آنت بھی ہوتی ہے۔

InsertClusterItems طریقہ کار (کئی ملتے جلتے میں سے ایک) مطلوبہ کلسٹر عنصر کے درخت میں متعلقہ شناخت کنندگان کے ساتھ چائلڈ عناصر کی فہرست شامل کرتا ہے۔
کلسٹر آئٹمز داخل کریں۔

proc InsertClusterItems {tree id} {
    set parent "cluster::$id"
    $tree insert $parent end -id "infobases::$id" -text "Информационные базы" -values "$id"
    $tree insert $parent end -id "servers::$id" -text "Рабочие серверы" -values "$id"
    $tree insert $parent end -id "admins::$id" -text "Администраторы" -values "$id"
    $tree insert $parent end -id "managers::$id" -text "Менеджеры кластера" -values $id
    $tree insert $parent end -id "processes::$id" -text "Рабочие процессы" -values "workprocess-all"
    $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "sessions-all"
    $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "blocks-all"
    $tree insert $parent end -id "connections::$id" -text "Соединения" -values "connections-all"
    $tree insert $parent end -id "profiles::$id" -text "Профили безопасности" -values $id
}

آپ اسی طرح کے طریقہ کار کو لاگو کرنے کے لیے دو اور اختیارات پر غور کر سکتے ہیں، جہاں یہ واضح طور پر نظر آئے گا کہ آپ کس طرح اصلاح کر سکتے ہیں اور دہرائے جانے والے حکموں سے چھٹکارا حاصل کر سکتے ہیں:

اس طریقہ کار میں، شامل کرنے اور جانچنے کا مسئلہ حل کیا جاتا ہے:

InsertBaseItems

proc InsertBaseItems {tree id} {
    set parent "infobase::$id"
    if { [$tree exists "sessions::$id"] == 0 } {
        $tree insert $parent end -id "sessions::$id" -text "Сеансы" -values "$id"
    }
    if { [$tree exists "locks::$id"] == 0 } {
        $tree insert $parent end -id "locks::$id" -text "Блокировки" -values "$id"
    }
    if { [$tree exists "connections::$id"] == 0 } {
        $tree insert $parent end -id "connections::$id" -text "Соединения" -values "$id"
    }
}

یہاں ایک زیادہ درست طریقہ ہے:

پروفائل آئٹمز داخل کریں۔

proc InsertProfileItems {tree id} {
    set parent "profile::$id"
    set lst {
        {dir "Виртуальные каталоги"}
        {com "Разрешённые COM-классы"}
        {addin "Внешние компоненты"}
        {module "Внешние отчёты и обработки"}
        {app "Разрешённые приложения"}
        {inet "Ресурсы интернет"}
    }
    foreach i $lst {
        append item [lindex $i 0] "::$id"
        if { [$tree exists $item] == 0 } {
            $tree insert $parent end -id $item -text [lindex $i 1] -values "$id"
        }
        unset item 
    }
}

ان کے درمیان فرق ایک لوپ کا استعمال ہے، جس میں بار بار کمانڈ کو عمل میں لایا جاتا ہے۔ کون سا طریقہ استعمال کرنا ہے یہ ڈویلپر کی صوابدید پر ہے۔

ہم نے عناصر کو شامل کرنے اور ڈیٹا کی بازیافت کا احاطہ کیا ہے، اب وقت آگیا ہے کہ ترمیم پر توجہ دیں۔ چونکہ، بنیادی طور پر، ایک ہی پیرامیٹرز کو ترمیم اور شامل کرنے کے لیے استعمال کیا جاتا ہے (معلومات کی بنیاد کے استثناء کے ساتھ)، اسی ڈائیلاگ فارم استعمال کیے جاتے ہیں۔ شامل کرنے کے طریقہ کار کو کال کرنے کا الگورتھم اس طرح لگتا ہے:

شامل کریں::$key->AddToplevel

اور اس طرح کی ترمیم کے لیے:

ترمیم کریں::$key->Add::$key->AddTopLevel

مثال کے طور پر، آئیے ایک کلسٹر میں ترمیم کرتے ہیں، یعنی درخت میں کلسٹر کے نام پر کلک کرنے کے بعد، ٹول بار (پنسل) میں ترمیم کا بٹن دبائیں اور متعلقہ فارم اسکرین پر ظاہر ہو جائے گا:

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں
ترمیم کریں::کلسٹر

proc Edit::cluster {tree host values} {
    global default lifetime_limit expiration_timeout session_fault_tolerance_level
    global max_memory_size max_memory_time_limit errors_count_threshold security_level
    global load_balancing_mode kill_problem_processes active_cluster 
    agent_user agent_pwd cluster_user cluster_pwd auth
    if {$cluster_user ne "" && $cluster_pwd ne ""} {
        set auth "--cluster-user=$cluster_user --cluster-pwd=$cluster_pwd"
    } else {
        set auth ""
    }
    # рисуем форму для кластера
    set frm [Add::cluster $tree $host $values]
    # меняем текст на метке
    $frm configure -text "Редактирование кластера"
    
    set active_cluster $values
    # получаем данные по выделенному кластеру
    set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
    # заполняем поля
    FormFieldsDataInsert $frm $lst
    # выключаем поля, редактирование которых запрещено
    $frm.ent_host configure -state disable
    $frm.ent_port configure -state disable
    # переназначаем обработчик
    .add.frm_btn.btn_ok configure -command {
        RunCommand "" "cluster update 
        --cluster=$active_cluster $auth 
        --name=[.add.frm.ent_name get] 
        --expiration-timeout=$expiration_timeout 
        --lifetime-limit=$lifetime_limit 
        --max-memory-size=$max_memory_size 
        --max-memory-time-limit=$max_memory_time_limit 
        --security-level=$security_level 
        --session-fault-tolerance-level=$session_fault_tolerance_level 
        --load-balancing-mode=$load_balancing_mode 
        --errors-count-threshold=$errors_count_threshold 
        --kill-problem-processes=$kill_problem_processes 
        $auth $host"
        $tree delete "cluster::$active_cluster"
        Run::server $tree $host ""
        destroy .add
    }
}

کوڈ میں دیے گئے تبصروں کی بنیاد پر، اصولی طور پر، سب کچھ واضح ہے، سوائے اس کے کہ بٹن ہینڈلر کوڈ کو اوور رائیڈ کر دیا گیا ہے اور ایک FormFieldsDataInsert طریقہ کار ہے جو ڈیٹا سے فیلڈز کو بھرتا ہے اور متغیرات کو شروع کرتا ہے:

FormFieldsDataInsert

proc FormFieldsDataInsert {frm lst} {
    foreach i [lindex $lst 0] {
        # получаем список параметров и значений
        if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
            # меняем символы
            regsub -all -- "-" [string trim $param] "_" entry_name
            # заполняем данными
            if [winfo exists $frm.ent_$entry_name] {
                $frm.ent_$entry_name delete 0 end
                $frm.ent_$entry_name insert end [string trim $value """]
            }
            if [winfo exists $frm.cb_$entry_name] {
                global $entry_name
                set $entry_name [string trim $value """]
            }
            # для чекбоксов меняем значения
            if [winfo exists $frm.check_$entry_name] {
                global $entry_name
                if {$value eq "0"} {
                    set $entry_name no
                } elseif {$value eq "1"} {
                    set $entry_name yes
                } else {
                    set $entry_name $value
                }
            }
        }
    }
}

اس طریقہ کار میں، tcl کا ایک اور فائدہ سامنے آیا - دوسرے متغیرات کی قدروں کو متغیر ناموں کے طور پر بدل دیا جاتا ہے۔ وہ. فارموں کو بھرنے اور متغیرات کی شروعات کو خودکار کرنے کے لیے، فیلڈز اور ویری ایبلز کے نام rac یوٹیلیٹی کے کمانڈ لائن سوئچز اور کمانڈ آؤٹ پٹ پیرامیٹرز کے ناموں کے ساتھ کچھ استثناء کے ساتھ مطابقت رکھتے ہیں - ڈیش کو انڈر سکور سے بدل دیا جاتا ہے۔ مثلاً مقررہ ملازمتوں سے انکار میدان سے میل کھاتا ہے۔ ent_scheduled_jobs_deny اور متغیر شیڈولڈ_نوکریاں_منکر.

شامل کرنے اور ترمیم کرنے کے فارم فیلڈز کی ساخت میں مختلف ہو سکتے ہیں، مثال کے طور پر، معلومات کی بنیاد کے ساتھ کام کرنا:

معلومات کی حفاظت کو شامل کرنا

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

معلومات کی حفاظت میں ترمیم کرنا

1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

ترمیم کے طریقہ کار Edit::infobase میں، فارم میں مطلوبہ فیلڈز شامل کیے جاتے ہیں؛ اس لیے میں اسے یہاں پیش نہیں کرتا۔

مشابہت سے، دیگر عناصر کے لیے شامل کرنے، ترمیم کرنے، حذف کرنے کے طریقہ کار کو لاگو کیا جاتا ہے۔

چونکہ یوٹیلیٹی کے آپریشن کا مطلب لامحدود تعداد میں سرورز، کلسٹرز، انفارمیشن بیسز وغیرہ ہے، اس بات کا تعین کرنے کے لیے کہ کون سا کلسٹر کس سرور یا انفارمیشن سیکیورٹی سسٹم سے تعلق رکھتا ہے، اس لیے کئی عالمی متغیرات متعارف کرائے گئے ہیں، جن کی قدریں ہر ایک کو متعین کی گئی ہیں۔ جب آپ درخت کے عناصر پر کلک کرتے ہیں۔ وہ. طریقہ کار بار بار تمام بنیادی عناصر کے ذریعے چلتا ہے اور متغیرات کو سیٹ کرتا ہے:

GlobalVarFromTreeItems سیٹ کریں۔

proc SetGlobalVarFromTreeItems {tree id} {
    global host server active_cluster infobase
    set parent [$tree parent $id]
    set values [$tree item $id -values]
    set key [lindex [split $id "::"] 0]
    switch -- $key {
        server {set host $values}
        work_server {set server $values}
        cluster {set active_cluster $values}
        infobase {set infobase $values}
    }
    if {$parent eq ""} {
        return
    } else {
        SetGlobalVarFromTreeItems $tree $parent
    }
}

1C کلسٹر آپ کو اجازت کے ساتھ یا اس کے بغیر کام کرنے کی اجازت دیتا ہے۔ ایڈمنسٹریٹرز کی دو قسمیں ہیں - کلسٹر ایجنٹ ایڈمنسٹریٹر اور کلسٹر ایڈمنسٹریٹر۔ اس کے مطابق، درست آپریشن کے لیے، ایڈمنسٹریٹر لاگ ان اور پاس ورڈ پر مشتمل 4 مزید عالمی متغیرات متعارف کرائے گئے۔ وہ. اگر کلسٹر میں ایڈمنسٹریٹر اکاؤنٹ ہے تو، آپ کا لاگ ان اور پاس ورڈ درج کرنے کے لیے ایک ڈائیلاگ ظاہر ہوگا، ڈیٹا کو میموری میں محفوظ کیا جائے گا اور متعلقہ کلسٹر کے لیے ہر کمانڈ میں داخل کیا جائے گا۔

یہ غلطی سے نمٹنے کے طریقہ کار کی ذمہ داری ہے۔

ایرر پارسنگ

proc ErrorParcing {err opt} {
    global cluster_user cluster_pwd agent_user agent_pwd
        switch -regexp -- $err {
        "Cluster administrator is not authenticated" {
            AuthorisationDialog "Администратор кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set cluster_user [.auth_win.frm.ent_name get]
                set cluster_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
            #RunCommand $root $par
        }
        "Central server administrator is not authenticated" {
            AuthorisationDialog "Администратор агента кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set agent_user [.auth_win.frm.ent_name get]
                set agent_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
        }
        "Администратор кластера не аутентифицирован" {
            AuthorisationDialog "Администратор кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set cluster_user [.auth_win.frm.ent_name get]
                set cluster_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
            #RunCommand $root $par
        }
        "Администратор центрального сервера не аутентифицирован" {
            AuthorisationDialog "Администратор агента кластера"
            .auth_win.frm_btn.btn_ok configure -command {
                set agent_user [.auth_win.frm.ent_name get]
                set agent_pwd [.auth_win.frm.ent_pwd get]
                destroy .auth_win
            }
        }
        (.+) {
            tk_messageBox -type ok -icon error -message "$err"
        }
    }
}

وہ. اس بات پر منحصر ہے کہ کمانڈ کیا لوٹتا ہے، ردعمل اسی کے مطابق ہوگا۔

اس وقت، تقریباً 95 فیصد فعالیت کو لاگو کیا جا چکا ہے، جو کچھ باقی ہے وہ سیکیورٹی پروفائلز کے ساتھ کام کو نافذ کرنا اور اس کی جانچ کرنا ہے =)۔ بس۔ میں ٹوٹی پھوٹی کہانی کے لیے معذرت خواہ ہوں۔

کوڈ روایتی طور پر دستیاب ہے۔ یہاں.

اپ ڈیٹ: میں نے سیکیورٹی پروفائلز کے ساتھ کام ختم کر دیا۔ اب فعالیت 100٪ نافذ ہے۔

اپ ڈیٹ 2: انگریزی اور روسی میں لوکلائزیشن کو شامل کیا گیا ہے، win7 میں کام کا تجربہ کیا گیا ہے۔
1C RAC کے لیے GUI لکھنا، یا پھر Tcl/Tk کے بارے میں

ماخذ: www.habr.com

نیا تبصرہ شامل کریں