Ndërsa thellohem në temën e produkteve të 1C në mjedisin linux, u zbulua një mangësi — mungesa e një mjeti grafikor shumëplatformatik komod për menaxhimin e grupeve të serverëve 1C. Prandaj, vendosa ta riparoj këtë mangësi duke shkruar një GUI për utilitarin konsolar rac. Gjuha e zhvillimit u zgjodh Tcl/Tk, pasi mendoj se është më e përshtatshme për këtë detyrë. Dhe këtu, disa aspekte interesante të zgjidhjes dua t'i paraqes në këtë material.
Për funksionimin do të nevojiten distribuera Tcl/Tk dhe 1C. Dhe pasi vendosa të shfrytëzoj sa më shumë mundësitë e paketës bazë Tcl/Tk pa përdorur paketa të jashtme, do të nevojitet versi 8.6.7, e cila përfshin ttk — paketë me elemente grafike shtesë, nga të cilat do të kemi nevojë kryesisht për ttk::TreeView, që lejon shfaqjen e të dhënave si në formën e një strukture pemore ashtu edhe në formën e një tabele (listë). Gjithashtu, në versionin e ri është ristrukturuar puna me përjashtimet (komanda try, e cila përdoret në projekt gjatë ekzekutimit të komandave të jashtme).
Projekti përbëhet nga disa skedinja (ndonëse asgjë nuk e pengon të bësh gjithçka një herë):
rac_gui.cfg — konfigurimi default
rac_gui.tcl — skripti kryesor i nisjes
Në katalogun lib ndodhen skedarët që ngarkohen automatikisht gjatë nisjes:
function.tcl — skedari me procedure
gui.tcl — ndërfaqja kryesore grafike
images.tcl — biblioteka e imazheve në base64
Skedari rac_gui.tcl në të vërtetë nis interpretorin, inicializon variablat, ngarkon modulët, konfigurimet etj. Përmbajtja e skedarit me komentet:
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"
}
}Pas ngarkimit të gjithçkaje që nevojitet dhe verifikimit të pranishmërisë së utilitarit rac, do të hapet një dritare grafike. Ndërfaqja e programit përbëhet nga tre elemente:
Bara e instrumenteve, pema dhe lista
Përmbajtja e 'pemës' e kam bërë sa më të ngjashme me komponentin standard të Windows nga 1C.
Kodi kryesor që formon këtë dritare ndodhet në skedarin
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
Algoritmi për të punuar me programin është si më poshtë:
1. Në fillim, duhet të shtoni serverin kryesor të grumbullit (dmth. serverin e menaxhimit të grumbullit (në linux menaxhimi fillon me komandën '/opt/1C/v8.3/x86_64/ras cluster —daemon')).
Për këtë klikoni butonin '+' dhe në dritaren e hapur, futni adresën e serverit dhe portin:

Më pas, në pemë do të shfaqet serveri ynë, duke e klikur mbi të, do të hapet lista e klasterëve ose do të shfaqet një gabim lidhjeje.
2. Duke klikuar mbi emrin e klasterit do të hapet lista e funksioneve të disponueshme për të.
3.…
Pra, për të shtuar një klaster të ri, zgjidhim një nga ata të disponueshëm në listë dhe klikojmë butonin «+» në panelin e mjetit, dhe do të shfaqet një dialog për shtimin e të riut:
Butonat në panelin e mjetit kryejnë funksione në varësi të kontekstit, pra, në varësi të asaj që është zgjedhur në pemë ose listë, do të kryhet procedura përkatëse.
Le të shqyrtojmë me shembullin e butonit të shtimit («+»):
Kodi që formon butonin:
ttk::button $frm_tool.btn_add -command Add -image add_grey_32Këtu shohim se kur të klikojmë butonin do të ekzekutohet procedura «Add», kodi i saj:
proc Add {} {
global active_cluster host
# Përcaktojmë identifikuesin e elementit të zgjedhur
set id [.frm_tree.tree selection]
# Përcaktojmë vlerën e këtij elementi
set values [.frm_tree.tree item [.frm_tree.tree selection] -values]
set key [lindex [split $id "::"] 0]
# në varësi të asaj që kemi zgjedhur do të nisë procedura e duhur
if {$key eq "" || $key eq "server"} {
set host [ Add::server ]
return
}
Add::$key .frm_tree.tree $host $values
}Këtu shfaqet një nga avantazhet e Tikly - si emër procedure mund të kaloni vlerën e një variabli:
Add::$key .frm_tree.tree $host $valuesPra, për shembull, nëse klikojmë mbi serverin kryesor dhe shtypim «+», do të aktivizohet procedura Add::server; nëse është klasteri - Add::cluster, e kështu me radhë (për nga ku vijnë «çelësat» e nevojshëm do të shkruaj më poshtë); procedurat e përmendura vizatojnë elementet grafike përkatëse kontekstit.
Siç mund ta keni vënë re, format janë të ngjashme në stil - kjo nuk është e çuditshme, pasi ato shfaqen nga një procedurë, konkretisht skeleti kryesor i formës (dritarja, butonat, imazhi, etiketa), emri i procedurës AddTopLevel
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
# etiketë me ikonë
ttk::label $win_name.lbl -image $img
# kornizë me fusha hyrëse
set frm [ttk::labelframe $win_name.frm -text $lbl -labelanchor nw]
grid columnconfigure $frm 0 -weight 1
grid rowconfigure $frm 0 -weight 1
# kornizë dhe butona
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
}
Parametrat e thirrjes: titulli, emri i imazhit për ikonën nga biblioteka (lib/images.tcl) dhe parametrin opsional të emrit të dritares (në mënyrë që të paracaktuar është .add). Pra, nëse marrim si shembuj të mësipërm për shtimin e serverit kryesor dhe klasterit, thirrja do të jetë përkatësisht:
AddToplevel "Shtimi i serverit kryesor" server_grey_64ose
AddToplevel "Shtimi i klasterit" cluster_grey_64Dhe duke vazhduar me këto shembuj, do të tregoj procedurat që nxjerrin dialogët për shtimin e serverit ose klasterit.
Add::server
proc Add::server {} {
global default
# shfaqim formularin kryesor
set frm [AddToplevel "Shtimi i serverit kryesor" server_grey_64]
# shtojmë etiketat dhe fushat e inputit në këtë formular
label $frm.lbl_host -text "Adresa e serverit"
entry $frm.ent_host
label $frm.lbl_port -text "Porti"
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]
# ripërdorim menaxherin e klikimit të butonit
.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
}Add::cluster
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 ""
}
# vendosim variablat globale ()
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 "Shtimi i klasterit" cluster_grey_64]
label $frm.lbl_host -text "Adresa e serverit kryesor"
entry $frm.ent_host
label $frm.lbl_port -text "Porti"
entry $frm.ent_port
$frm.ent_port insert end $default(port)
label $frm.lbl_name -text "Emri i klasterit"
entry $frm.ent_name
label $frm.lbl_secure_connect -text "Lidhje e sigurtë"
ttk::combobox $frm.cb_security_level -textvariable security_level -values $default(security_level)
label $frm.lbl_expiration_timeout -text "Prisni proceset e fikura për:"
entry $frm.ent_expiration_timeout -textvariable expiration_timeout
label $frm.lbl_session_fault_tolerance_level -text "Niveli i tolerancës për dështimin e seancave"
entry $frm.ent_session_fault_tolerance_level -textvariable session_fault_tolerance_level
label $frm.lbl_load_balancing_mode -text "Mënyra e shpërndarjes së ngarkesës"
ttk::combobox $frm.cb_load_balancing_mode -textvariable load_balancing_mode
-values $default(load_balancing_mode)
label $frm.lbl_errors_count_threshold -text "Pragu i pranueshëm për numrin e gabimeve të serverit, %"
entry $frm.ent_errors_count_threshold -textvariable errors_count_threshold
label $frm.lbl_processes -text "Proceset punuese:"
label $frm.lbl_lifetime_limit -text "Periudha e ri-përgjimit, sekondë."
entry $frm.ent_lifetime_limit -textvariable lifetime_limit
label $frm.lbl_max_memory_size -text "Kapaciteti maksimal i memories, KB"
entry $frm.ent_max_memory_size -textvariable max_memory_size
label $frm.lbl_max_memory_time_limit -text "Koha e tejkalimit të kapacitetit maksimal të memories, sekondë."
entry $frm.ent_max_memory_time_limit -textvariable max_memory_time_limit
label $frm.lbl_kill_problem_processes -justify left -anchor nw -text "Mbaroni me forcë proceset problematike"
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
# rikonstruktojmë handler-in
.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
}Kur krahasoni kodin e këtyre procedurave, ndryshimi është i dukshëm me sy të zakonshëm, do të fokusohem tek trajtuesi i butonit «Ok». Në Tk, karakteristikat e elementeve grafikë mund të rregullohen gjatë ekzekutimit të programit përmes opsionit konfiguro. Për shembull, urdhri fillestar për të prodhuar butonin:
ttk::button $frm_btn.btn_ok -image ok_grey_24 -command { }Por në format tona, komanda varet nga funksionaliteti që kërkohet:
.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
}
Në shembullin e mësipërm, butoni «zbaton» nisjen e procedurës për shtimin e klasterit.
Këtu duhet të bëjmë një kthesë në punën me elementët grafikë në Tk — për elemente të ndryshme të hyrjes së të dhënave (entry, combobox, checkbutton, etj.) është futur një parametër siç është variabla tekstore (textvariable):
entry $frm.ent_lifetime_limit -textvariable lifetime_limitKyçja është e përcaktuar në hapësirën globale dhe përmban vlerën e tanishme të futur. Për të marrë tekstin e futur në fushë, thjesht duhet të lexoni vlerën e përkatëses, natyrisht nëse është e përcaktuar kur është krijuar elementi.
Metoda e dytë për të marrë tekstin e futur (për elementi të tipit entry) është përdorimi i komandës get:
.add.frm.ent_name getTë dy këto metoda mund të shihen në kodin e mësipërm.
Shtypja e kësaj butoni, në këtë rast, nxit procedurën RunCommand me radhen e formuar të komandës për të shtuar klasterin në terma rac:
/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:1545Këtu kemi arritur në komandën kryesore, e cila menaxhon nisjen e rac me parametrat që na nevojiten, gjithashtu analizon daljet e komandave në lista dhe i kthen, nëse është e nevojshme:
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
# hapim kanali në mod instiktiv
# $rac - një komande me rrugë të plotë
# $par - çelësat dhe opsionet e formuara për nisjen
set pipe [open "|$rac_cmd $par" "r"]
try {
set lst ""
set l ""
# outputi i komandës shtohet në listën e listave
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} {
# Aktivizohet menaxhimi i gabimeve
ErrorParcing $result $options
return ""
}
}Pasi të futen të dhënat e serverit kryesor, ai do të shtohet në pemë, për këtë, në procedurën e mësipërme Add:server, përgjigjet kodi i mëposhtëm:
.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"Tani duke klikuar mbi emrin e serverit në pemë, ne do të marrim një listë të grupeve të menaxhuara nga ai server, dhe duke klikuar në grup, do të marrim një listë të elementeve të grupit (servera, bazave të informacionit etj.). Kjo është realizuar në procedurën TreePress (fajl lib/function.tcl):
proc TreePress {tree} {
global host server active_cluster infobase
# përcaktojmë elementin e zgjedhur
set id [$tree selection]
# vendosim variablat globale të nevojshme
SetGlobalVarFromTreeItems $tree $id
# përcaktojmë çelësin dhe vlerën, dmth llojin e elementit të zgjedhur
set values [$tree item $id -values]
set key [lindex [split $id "::"] 0]
# dhe në varësi të asaj që zgjodhët, do të aktivizohet procedura përkatëse
# në hapësirën Run
Run::$key $tree $host $values
}Prandaj, për serverin kryesor do të aktivizohet Run::server (për klasterin — Run::cluster, për serverin e punës — Run::work_server, etj.). Pra, vlera e variables $key është pjesa e emrit të elementit të pemës, e cila përcaktohet nga opsioni -id.
Le të tregojmë për procedurën
Run::server
proc Run::server {tree host values} {
# marrim listën e grupeve për serverin e kërkuar
set lst [RunCommand server::$host "cluster list $host"]
if {$lst eq ""} {return}
set l [lindex $lst 0]
# heqim gjërat e tepërta nga lista
.frm_work.tree_work delete [ .frm_work.tree_work children {}]
# lexojmë listën
foreach cluster_list $lst {
# Plotësuojmë listën me vlerat e marra
InsertItemsWorkList $cluster_list
# përpunojmë daljen (listën) për të shtuar të dhënat në pemë
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]]
}
}
}
# shtojmë grupet në pemë
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"
# shtojmë elementet në grup
InsertClusterItems $tree $id
}
}
if { [$tree exists "agent_admins::$id"] == 0 } {
$tree insert "server::$host" end -id "agent_admins::$id" -text "Administratoret" -values "$id"
#InsertClusterItems $tree $id
}
}Kjo procedurë përpunon atë që është marrë nga serveri përmes komandës RunCommand dhe shton gjëra të ndryshme në pemë — grupe, elemente të ndryshme rrënjore (bazat, serverë punues, seanca dhe kështu me radhë). Nëse shikon me kujdes, brenda mund të vëshhtë një thirrje të procedurës InsertItemsWorkList. Kjo përdoret për të shtuar elemente në listën grafike, duke përpunuar daljen e utilitarit të konsolës rac, i cili më parë ishte kthyer në format liste në variablën $lst. Kjo është një listë e listave, që përmban çiftet e elementeve të ndara me dy pika.
Për shembull, lista e lidhjeve të klasterit:
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
Në pamjen grafike, kjo do të dukej më shumë kështu:
Procedura e përmendur më sipër identifikon emrat e elementeve për titullin dhe të dhënat për plotësimin e tabelës:
InsertItemsWorkList
proc InsertItemsWorkList {lst} {
global work_list_row_count
# vendosja e alternimit të ngjyrave për rresht
if [expr $work_list_row_count % 2] {
set tag dark
} else {
set tag light
}
# analiza e rreshtave në çifte çelës - vlerë
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]
}
}
# plotësimi i tabelës
.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
# vendosja e titujve
foreach j $column_list {
.frm_work.tree_work heading $j -text $j
}
incr work_list_row_count
}Këtu, në vend të komandës së thjeshtë [split $str «:»], e cila ndan vargun në elemente të ndara nga «:» dhe kthen një listë, është aplikuar një shprehje rregullore, pasi disa elemente përmbajnë gjithashtu dy pika.
Procedura InsertClusterItems (një nga disa të ngjashme) thjesht shton në pemë për elementin e nevojshëm klustërin e listës së elementeve fëmijë me identifikuesit përkatës.
InsertClusterItems
proc InsertClusterItems {tree id} {
set parent "cluster::$id"
$tree insert $parent end -id "infobases::$id" -text "Baza informative" -values "$id"
$tree insert $parent end -id "servers::$id" -text "Serverët aktiv" -values "$id"
$tree insert $parent end -id "admins::$id" -text "Administratorët" -values "$id"
$tree insert $parent end -id "managers::$id" -text "Menaxherët e klasterit" -values $id
$tree insert $parent end -id "processes::$id" -text "Procese aktive" -values "workprocess-all"
$tree insert $parent end -id "sessions::$id" -text "Seanca" -values "sessions-all"
$tree insert $parent end -id "locks::$id" -text "Bllokimet" -values "blocks-all"
$tree insert $parent end -id "connections::$id" -text "Konekcionet" -values "connections-all"
$tree insert $parent end -id "profiles::$id" -text "Profilet e sigurisë" -values $id
}
Mund të shqyrtojmë edhe dy variante të tjera të realizimit të një procedure të tillë, ku do të jetë qartë si mund të optimizohet dhe të eliminohen komandat e përsëritura:
Në këtë procedurë, shtimi dhe verifikimi janë zgjidhur në mënyrë të drejtpërdrejtë:
InsertBaseItems
proc InsertBaseItems {tree id} {
set parent "infobase::$id"
if { [$tree exists "sessions::$id"] == 0 } {
$tree insert $parent end -id "sessions::$id" -text "Seanca" -values "$id"
}
if { [$tree exists "locks::$id"] == 0 } {
$tree insert $parent end -id "locks::$id" -text "Bllokimet" -values "$id"
}
if { [$tree exists "connections::$id"] == 0 } {
$tree insert $parent end -id "connections::$id" -text "Konekcionet" -values "$id"
}
}
Këtu qasja është më e saktë:
InsertProfileItems
proc InsertProfileItems {tree id} {
set parent "profile::$id"
set lst {
{dir "Kataloget Virtual"}
{com "Klasa COM të lejuara"}
{addin "Komponentët e jashtëm"}
{module "Raportet dhe përpunimet e jashtme"}
{app "Aplikacionet e lejuara"}
{inet "Burimet e internetit"}
}
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
}
}Dallimi midis tyre është në aplikimin e ciklit, ku ekzekutohet komanda e përsëritur (komandat). Cila qasje të përdoret — kjo është në discretion të zhvilluesit.
Ne kemi shqyrtuar shtimin e elementeve dhe marrjen e të dhënave, tani është koha të ndalemi te redaktimi. Duke qenë se, kryesisht, për redaktimin dhe shtimin përdoren të njëjtat parametra (përjashtimi është baza informative), forma dialoguese gjithashtu përdoren të njëjta. Algoritmi i thirrjes së procedurave për shtimin duket kështu:
Add::$key->AddToplevel
Dhe për redaktimin kështu:
Edit::$key->Add::$key->AddTopLevel
Për shembull, le të marrim redaktimin e grupit, domethënë duke klikuar në emrin e grupit në pemën e informacionit, shtypim butonin e redaktimit në panelin e mjeteve (majtë) dhe forma përkatëse do të shfaqet në ekran:

Edit::cluster
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 ""
}
# krijo formën për klasterin
set frm [Add::cluster $tree $host $values]
# ndrysho tekstin në etiketë
$frm configure -text "Editimi i klasterit"
set active_cluster $values
# merr të dhënat për klasterin e përzgjedhur
set lst [RunCommand cluster::$values "cluster info --cluster=$active_cluster $host"]
# plotëso fushat
FormFieldsDataInsert $frm $lst
# çaktivizo fushat, redaktimi i të cilave është i ndaluar
$frm.ent_host configure -state disable
$frm.ent_port configure -state disable
# riemëro menaxherin
.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
}
}Në përgjithësi, gjithçka është e qartë nga komentet në kod, përveç faktit se kodi për procesin e butonit është pastruar dhe ka një procedurë FormFieldsDataInsert, e cila mbush fushat me të dhëna dhe inicializon variablat:
FormFieldsDataInsert
proc FormFieldsDataInsert {frm lst} {
foreach i [lindex $lst 0] {
# marrim listën e parametrave dhe vlerave
if [regexp -nocase -all -- {(D+)(s*?|)(:)(s*?|)(.*)} $i match param v2 v3 v4 value] {
# ndërruam simbolet
regsub -all -- "-" [string trim $param] "_" entry_name
# mbushim me të dhëna
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 """]
}
# për kutitë kontrolluese ndërruam vlerat
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
}
}
}
}
}
Në këtë procedurë ka dalë njëAvantazh tjetër i tcl — si emra variablash, vendosen vlerat e variablave të tjerë. Pra, për automatizimin e plotësimit të formave dhe inicializimin e variablave, emrat e fushave dhe variablave përputhen me çelësat e komandës së utilitarit rac dhe emrat e parametrave të daljes së komandave me disa përjashtime — minus shënohet si nënvizim. Për shembull, scheduled-jobs-deny përputhet me fushën ent_scheduled_jobs_deny dhe me variablën scheduled_jobs_deny.
Format e shtimit dhe redaktimit mund të ndryshojnë për nga përbërja e fushave, për shembull, puna me bazën informuese:
Shtimi i IB

Redaktimi i IB

Në procedurën e redaktimit Edit::infobase, në formë shtohen fushat e nevojshme, kodi është i volumshëm prandaj këtu nuk e përmend.
Procedurat e shtimit, redaktimit, fshirjes janë realizuar në mënyrë analogjike edhe për elementet e tjera.
Duke puna e utilitarit përfshin një numër të pakufizuar serverash, klasterash, bazash informacioni etj., për të përcaktuar se cilit klaster i përket cili server ose baza e informacionit, janë futur disa variabla globalë, vlerat e të cilëve caktohen për çdo klikim mbi elementet e pemës. Kjo do të thotë se procedura kalon rekursivisht nëpër të gjithë elementët prind dhe cakton variablat:
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
}
}
Klienti 1C mund të punojnë si me autorizim ashtu edhe pa. Ekzistojnë dy lloje administratorësh — administratori i agjentit të klientit dhe administratori i klientit. Për këtë arsye, janë futur edhe 4 variabla globalë, të cilët përmbajnë emrin dhe fjalëkalimin e administratorit. Pra, nëse në klient ka një llogari administratori, do të shfaqet një dialog për të futur emrin dhe fjalëkalimin, të dhënat do të ruhen në memorie dhe do të përdoren në çdo komandë për klientin e përkatshëm.
Kjo është përgjegjësia e procedurës së trajtimit të gabimeve
ErrorParcing
proc ErrorParcing {err opt} {
global cluster_user cluster_pwd agent_user agent_pwd
switch -regexp -- $err {
"Administrator i grupit nuk është autentikuar" {
AuthorisationDialog "Administrator i grupit"
.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
}
"Administrator i serverit qendror nuk është autentikuar" {
AuthorisationDialog "Administrator i agjentit të grupit"
.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
}
}
"Administrator i grupit nuk është autentikuar" {
AuthorisationDialog "Administrator i grupit"
.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
}
"Administrator i serverit qendror nuk është autentikuar" {
AuthorisationDialog "Administrator i agjentit të grupit"
.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"
}
}
}Pra ndaj, në varësi të asaj që kthen komanda, do të jetë edhe reagimi.
Aktualisht, funksionaliteti është realizuar rreth 95%, mbetet të realizohet puna me profilet e sigurisë dhe ta testojmë =). Kjo është gjithçka. Më vjen keq për rrëfimin e ngjeshur.
Kodi, sipas traditës, është i disponueshëm. .
Përditësim: Perfekte këmba e punës me profilin e sigurisë. Tani funksionaliteti është realizuar në 100%.
Përditësim 2: është shtuar lokalizimi në anglisht dhe rusisht, funksionimi është verifikuar në win7.

Burimi: habr.com
