1C RAC-д зориулсан GUI бичих эсвэл Tcl/Tk-ийн талаар дахин бичих

Линукс орчинд 1С бүтээгдэхүүн хэрхэн ажилладаг тухай сэдвийг судалж үзэхэд нэг сул тал нь 1С серверүүдийн кластерийг удирдахад тохиромжтой график олон платформ хэрэгсэл байхгүй байгааг олж мэдсэн. Мөн rac консолын хэрэгсэлд GUI бичих замаар энэ дутагдлыг засахаар шийдсэн. Tcl/tk-ийг хөгжүүлэх хэлээр сонгосон бөгөөд миний бодлоор энэ ажилд хамгийн тохиромжтой. Тиймээс би энэ материалд шийдлийн зарим сонирхолтой талуудыг танилцуулахыг хүсч байна.

Ажиллахын тулд танд tcl/tk болон 1C түгээлт хэрэгтэй болно. Гуравдагч талын багцыг ашиглахгүйгээр үндсэн tcl/tk хүргэлтийн боломжуудыг бүрэн ашиглахаар шийдсэн тул надад ttk-г багтаасан 8.6.7 хувилбар хэрэгтэй болно - нэмэлт график элементүүдтэй багц, үүнд бидэнд ихэвчлэн ttk хэрэгтэй. ::TreeView нь модны бүтэц болон хүснэгт (жагсаалт) хэлбэрээр өгөгдлийг харуулах боломжийг олгодог. Мөн шинэ хувилбарт үл хамаарах ажлыг дахин боловсруулсан (гадны командуудыг ажиллуулах үед төсөлд ашигладаг try команд).

Төсөл нь хэд хэдэн файлаас бүрддэг (хэдийгээр бүгдийг нэг дор хийхэд юу ч саад болохгүй):

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"
    }    
}

Шаардлагатай бүх зүйлийг татаж аваад rac хэрэгсэл байгаа эсэхийг шалгасны дараа график цонх нээгдэнэ. Програмын интерфейс нь гурван элементээс бүрдэнэ.

Хэрэгслийн мөр, мод, жагсаалт

Би "мод" -ын агуулгыг 1С-ийн стандарт Windows төхөөрөмжтэй аль болох төстэй болгосон.

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.…

Гэх мэтчилэн, i.e. Шинэ кластер нэмэхийн тулд жагсаалтад байгаа аль нэгийг сонгоод, багаж самбар дээрх "+" товчийг дарснаар шинээр нэмэх харилцах цонх гарч ирнэ.

1C RAC-д зориулсан GUI бичих эсвэл Tcl/Tk-ийн талаар дахин бичих

Хэрэгслийн самбар дээрх товчлуурууд нь контекстээс хамааран функцийг гүйцэтгэдэг, i.e. Мод эсвэл жагсаалтын аль элементийг сонгохоос хамааран нэг буюу өөр процедурыг гүйцэтгэнэ.

Нэмэх товчлуурын жишээг харцгаая ("+"):

Товчлуур үүсгэх код:

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

Энд бид товчлуурыг дарахад "Нэмэх" процедурыг гүйцэтгэх болно, түүний код:

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
}

Гижиглэхийн нэг давуу тал нь энд байна: та хувьсагчийн утгыг процедурын нэрээр дамжуулж болно:

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
}

Эдгээр процедурын кодыг харьцуулахдаа ялгаа нь нүцгэн нүдээр харагддаг тул би "Ok" товчлуурын зохицуулагч дээр анхаарлаа хандуулах болно. 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

Эдгээр хоёр аргыг дээрх кодоос харж болно.

Энэ товчийг дарснаар энэ тохиолдолд rac-ийн хувьд кластер нэмэхийн тулд үүсгэсэн тушаалын мөр бүхий 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

Одоо бид шаардлагатай параметрүүдээр rac-ыг эхлүүлэхийг хянадаг үндсэн команд руу орж, тушаалуудын гаралтыг жагсаалт болгон задлан шинжилж, шаардлагатай бол буцаана.

RunCommand

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 процедурт (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 гэх мэт) ажиллах болно. Тэдгээр. $ түлхүүр хувьсагчийн утга нь сонголтоор заасан модны элементийн нэрний нэг хэсэг юм - гэж хэлсэн.

Процедурт анхаарлаа хандуулцгаая

:: серверийг ажиллуул

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 процедурын дуудлагыг анзаарах болно. Энэ нь өмнө нь $lst хувьсагч руу жагсаалт болгон буцаасан rac console хэрэгслийн гаралтыг боловсруулах замаар график жагсаалтад элементүүдийг нэмэхэд хэрэглэгддэг. Энэ бол хоёр цэгээр тусгаарлагдсан хос элементүүдийг агуулсан жагсаалтын жагсаалт юм.

Жишээлбэл, кластер холболтуудын жагсаалт:

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 процедур (хэд хэдэн ижил төстэй аргуудын нэг) нь шаардлагатай кластер элементийн модонд харгалзах танигчтай хүүхдийн элементүүдийн жагсаалтыг нэмдэг.
ClusterItems оруулах

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"
    }
}

Энд илүү зөв хандлага байна:

InsertProfileItems

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 
    }
}

Тэдний хоорондох ялгаа нь давтагдсан тушаалуудыг гүйцэтгэх давталт ашиглах явдал юм. Аль аргыг ашиглах нь хөгжүүлэгчийн үзэмжээр байна.

Элемент нэмэх, өгөгдөл сэргээх талаар бид тайлбарласан тул засварлахад анхаарлаа хандуулах цаг болжээ. Үндсэндээ ижил параметрүүдийг засварлах, нэмэхэд ашигладаг тул (мэдээллийн санг эс тооцвол) ижил харилцах хэлбэрийг ашигладаг. Нэмэх процедурыг дуудах алгоритм нь дараах байдалтай байна.

Нэмэх ::$түлхүүр->Товчлуур нэмэх

Мөн иймэрхүү засварлахын тулд:

Edit::$key->Add::$key->AddTopLevel

Жишээлбэл, кластер засварлахыг авч үзье, i.e. Модон дээрх кластерын нэр дээр дарсны дараа хэрэгслийн самбар (харандаа) дахь засварлах товчийг дарахад харгалзах маягт дэлгэц дээр гарч ирнэ.

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 хэрэгслийн командын мөрийн унтраалга, командын гаралтын параметрүүдийн нэрстэй тохирч байвал зураасыг доогуур зураасаар солино. Жишээ нь хуваарьт ажлын байрыг үгүйсгэх талбайтай таарч байна хуваарьт_ажлыг_татгалзах болон хувьсагч хуваарьт_ажлыг_татгалзах.

Нэмэх, засварлах маягтууд нь талбаруудын найрлагад ялгаатай байж болно, жишээлбэл, мэдээллийн сантай ажиллах:

Мэдээллийн аюулгүй байдлыг нэмэх

1C RAC-д зориулсан GUI бичих эсвэл Tcl/Tk-ийн талаар дахин бичих

Мэдээллийн аюулгүй байдлыг засварлах

1C RAC-д зориулсан GUI бичих эсвэл Tcl/Tk-ийн талаар дахин бичих

Засварлах :: infobase-д шаардлагатай талбаруудыг маягт дээр нэмсэн; код нь том хэмжээтэй тул би энд танилцуулахгүй байна.

Үүнтэй адилаар бусад элементүүдэд нэмэх, засварлах, устгах процедурыг хэрэгжүүлдэг.

Энэ хэрэгслийн ажиллагаа нь хязгааргүй тооны сервер, кластер, мэдээллийн бааз гэх мэтийг агуулдаг тул аль кластер аль сервер эсвэл мэдээллийн аюулгүй байдлын системд харьяалагддагийг тодорхойлохын тулд хэд хэдэн глобал хувьсагчийг нэвтрүүлсэн бөгөөд тэдгээрийн утгыг тус бүрээр нь тохируулсан болно. модны элементүүд дээр дарах цаг. Тэдгээр. Уг процедур нь бүх эх элементүүдээр дамжиж, хувьсагчдыг тохируулдаг:

SetGlobalVarFromTreeItems

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
    }
}

1С кластер нь зөвшөөрөлтэй эсвэл зөвшөөрөлгүйгээр ажиллах боломжийг олгодог. Кластер агент администратор ба кластер администратор гэсэн хоёр төрлийн администратор байдаг. Үүний дагуу зөв ажиллахын тулд администраторын нэвтрэх болон нууц үг агуулсан 4 глобал хувьсагчийг нэвтрүүлсэн. Тэдгээр. Хэрэв кластерт администраторын бүртгэл байгаа бол нэвтрэх болон нууц үгээ оруулах харилцах цонх гарч ирэх бөгөөд өгөгдлийг санах ойд хадгалж, холбогдох кластерын команд бүрт оруулах болно.

Энэ нь алдаа засах процедурын хариуцлага юм.

ErrorParcing

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

сэтгэгдэл нэмэх