عندما بحثت في موضوع منتجات 1C العاملة في لينكس ، اكتشفت عيبًا واحدًا - عدم وجود أداة رسومية متعددة المنصات ملائمة لإدارة مجموعة من خوادم 1C. وتقرر إصلاح هذا القصور عن طريق كتابة واجهة المستخدم الرسومية لسباق وحدة التحكم. تم اختيار لغة التطوير tcl / tk لأنها ، في رأيي ، هي الأنسب لهذه المهمة. والآن ، أريد أن أقدم بعض جوانب الحل المثيرة للاهتمام في هذه المادة.
للعمل ، تحتاج إلى توزيعات tcl / tk و 1 C. وبما أنني قررت تحقيق أقصى استفادة من إمكانات التسليم الأساسي لـ tcl / tk دون استخدام حزم الطرف الثالث ، فسأحتاج إلى الإصدار 8.6.7 ، والذي يتضمن ttk - وهي حزمة تحتوي على عناصر رسومية إضافية ، والتي سنحتاج بشكل رئيسي إلى 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" } }
بعد تنزيل كل ما هو مطلوب والتحقق من توفر الأداة المساعدة ، سيتم تشغيل نافذة رسومية. تتكون واجهة البرنامج من ثلاثة عناصر:
شريط الأدوات والشجرة والقائمة
لقد جعلت محتويات "الشجرة" متشابهة قدر الإمكان مع نافذة قياسية من 1C.

الكود الرئيسي الذي يشكل هذه النافذة موجود في الملف
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 الكتلة -" demon ")
للقيام بذلك ، انقر فوق الزر "+" وفي النافذة التي تفتح ، أدخل عنوان الخادم والمنفذ:

بعد ذلك ، يظهر خادمنا في الشجرة من خلال النقر فوق ، يتم فتح قائمة بالمجموعات أو يتم عرض خطأ في الاتصال.
2. بالضغط على اسم المجموعة ، سيتم فتح قائمة بالوظائف المتاحة لها.
3. ...
حسنًا وما إلى ذلك ، على سبيل المثال لإضافة مجموعة جديدة ، حدد أي مجموعة متاحة في القائمة واضغط على الزر + في شريط الأدوات وسيتم عرض مربع الحوار لإضافة مجموعة جديدة:

تؤدي الأزرار الموجودة في شريط الأدوات وظائف وفقًا للسياق ، أي يتم من خلاله تحديد عنصر الشجرة أو القائمة ، سيتم تنفيذ هذا الإجراء أو ذاك.
ضع في اعتبارك زر الإضافة ("+") كمثال:
رمز توليد الزر:
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 :: الكتلة وما إلى ذلك (سأكتب القليل عن مصدر "المفاتيح" الضرورية أدناه) ، ترسم الإجراءات المدرجة العناصر الرسومية المناسبة للسياق.
كما لاحظت ، فإن النماذج متشابهة في الأسلوب - وهذا ليس مفاجئًا ، لأنها معروضة في إجراء واحد ، وبشكل أكثر دقة الإطار الرئيسي للنموذج (النافذة ، الأزرار ، الصورة ، التسمية) ، اسم الإجراء
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 # 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 - للعناصر المختلفة لإدخال البيانات (الإدخال ، combobox ، checkbutton ، وما إلى ذلك) ، تم إدخال هذه المعلمة كمتغير نص (متغير نص):
entry $frm.ent_lifetime_limit -textvariable lifetime_limit
يتم تعريف هذا المتغير في مساحة الاسم العامة ويحتوي على القيمة الحالية التي تم إدخالها. على سبيل المثال من أجل الحصول على النص الذي تم إدخاله من الحقل ، ما عليك سوى قراءة القيمة المقابلة للمتغير (بالطبع ، شريطة أن يتم تحديده عند إنشاء العنصر).
الطريقة الثانية للحصول على النص الذي تم إدخاله (لعناصر إدخال النوع) هي استخدام الأمر get:
.add.frm.ent_name get
يمكن رؤية كلتا الطريقتين في الكود أعلاه.
يؤدي النقر فوق هذا الزر ، في هذه الحالة ، إلى بدء إجراء RunCommand بسطر الأوامر الذي تم إنشاؤه لإضافة مجموعة من حيث 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:1545
لذلك وصلنا إلى الأمر الرئيسي ، الذي يتحكم في تشغيل rac بالمعلمات التي نحتاجها ، كما يوزع إخراج الأوامر في قوائم ويعود إذا لزم الأمر:
أمر تشغيل 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 "" } }
بعد إدخال بيانات الخادم الرئيسي ، سيتم إضافتها إلى الشجرة ، لهذا ، في الإجراء السابق أعلاه: إجراء الخادم ، يكون الرمز التالي مسؤولاً:
.frm_tree.tree insert {} end -id "server::$host" -text "$host" -values "$host"
الآن ، بالضغط على اسم الخادم في الشجرة ، نحصل على قائمة بالمجموعات التي يديرها ذلك الخادم ، والنقر على مجموعة ، نحصل على قائمة بعناصر المجموعة (خوادم ، و infobase ، وما إلى ذلك). يتم تنفيذ ذلك في إجراء 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 :: الكتلة للكتلة ، Run :: work_server ، إلخ لخادم الإنتاج). على سبيل المثال تعد قيمة المتغير الرئيسي $ جزءًا من اسم عنصر الشجرة المحدد بواسطة الخيار
-id .
انتبه إلى الإجراء
تشغيل :: الخادم 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 المتغير. هذه قائمة بالقوائم التي تحتوي على أزواج من العناصر مفصولة بنقطتين.
على سبيل المثال ، قائمة اتصالات الكتلة:
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
في شكل رسوم بيانية ، سيبدو شيئًا مثل هذا:

يحدد الإجراء أعلاه أسماء عناصر الرأس والبيانات لملء الجدول:
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 (واحد من عدة عناصر متشابهة) قائمة بالعناصر الفرعية ذات المعرفات المقابلة لعنصر الكتلة المطلوب في الشجرة
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" } }
وهنا النهج أكثر صحة:
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 } }
الفرق بينهما هو استخدام الدورة التي يتم فيها تنفيذ الأوامر (الأوامر) المتكررة. الطريقة التي يتم استخدامها وفقًا لتقدير المطور.
فكرنا في إضافة العناصر وتلقي البيانات ؛ حان الوقت للتوقف عند التحرير. نظرًا لأنه ، بشكل أساسي ، يتم استخدام نفس المعلمات للتحرير والإضافة (الاستثناء هو infobase) ، فإن نماذج الحوار هي نفسها. يبدو إجراء استدعاء الإجراء للإضافة كما يلي:
Add :: $ key-> AddToplevelولتحرير مثل هذا:
تحرير :: $ key-> Add :: $ key-> AddTopLevelعلى سبيل المثال ، خذ تحرير كتلة ، أي من خلال النقر على اسم المجموعة في الشجرة ، اضغط على زر التحرير في شريط الأدوات (قلم رصاص) وسيتم عرض النموذج المقابل:

تحرير :: الكتلة 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 ومتغير المجدولة المجدولة .قد تختلف أشكال الإضافة والتحرير في تكوين الحقول ، على سبيل المثال ، العمل مع قاعدة المعلومات:إضافة IB
Editing IB
في إجراء التحرير Edit :: infobase ، تتم إضافة الحقول المطلوبة إلى النموذج ، لذلك لا أقدم رمزًا كبيرًا هنا.عن طريق القياس ، يتم تنفيذ إجراءات إضافة عناصر أخرى وتحريرها وحذفها.نظرًا لأن الأداة تتضمن عددًا غير محدود من الخوادم والمجموعات و 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 } }
تسمح لك مجموعة 1C بالعمل مع أو بدون إذن. هناك نوعان من المسؤولين - مسؤول عامل الكتلة ومسؤول الكتلة. وفقًا لذلك ، بالنسبة للعملية الصحيحة ، تم إدخال 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