Тема: Некоторые замечания и советы по использованию TechnologiCS АПИ

Проблема Как поднять скорость выполнения скрипта при работе с большим набором данных.

Здесь небольшие примеры, показывающие как можно значительно повысить скорость работы в таких случаях

Возьмите большой набор данных (>10 тысяч строк) и выполните такой пример (пробежка по набору данных)

1.1. Простая пробежка по набору данных( пример 1 )

Sub FormMacro_Test(TCSActiveModule)
  TCSActiveModule.First
  Call TCSApp.ShowMessageBox( "", "Start" )
  While Not TCSActiveModule.Eof
    Call TCSActiveModule.Next    
  Wend
End Sub

1.2 Простая пробежка по набору данных( пример 2, с большим ускорением )

Sub FormMacro_Test(TCSActiveModule)
  TCSActiveModule.First
  Call TCSApp.ShowMessageBox( "", "Start" )
  Call TCSActiveModule.BeginUpdate
  While Not TCSActiveModule.Eof
    Call TCSActiveModule.Next    
  Wend
  Call TCSActiveModule.EndUpdate
  Call TCSApp.ShowMessageBox( "", "End" )
End Sub


2.1. Чтение данных в цикле пробежки по данным

Sub FormMacro_Test(TCSActiveModule)
  TCSActiveModule.First
  Call TCSApp.ShowMessageBox( "", "Start" )
  Call TCSActiveModule.BeginUpdate
  While Not TCSActiveModule.Eof
    For I = 0 To TCSActiveModule.PropertiesCount-1
      If TCSActiveModule.Properties(i).IsSimpleType Then  D = TCSActiveModule.Properties(i).Value
    Next
    Call TCSActiveModule.Next    
  Wend
  Call TCSActiveModule.EndUpdate
  Call TCSApp.ShowMessageBox( "", "End" )
End Sub

2.2. Чтение данных в цикле пробежки по данным ( со значительным ускорением)

Sub FormMacro_Test(TCSActiveModule)
  TCSActiveModule.First
  Call TCSApp.ShowMessageBox( "", "Start" )
  Call TCSActiveModule.BeginUpdate
  Dim Props(100)
  For I = 0 To TCSActiveModule.PropertiesCount-1
    If TCSActiveModule.Properties(i).IsSimpleType Then 
        Set Props(I) = TCSActiveModule.Properties(i)
      Else 
        Set Props(I) = Nothing
    End If
  Next
  While Not TCSActiveModule.Eof
    For I = 0 To TCSActiveModule.PropertiesCount-1
      If Not Props(I) Is Nothing Then D = Props(I).Value
    Next
    Call TCSActiveModule.Next    
  Wend
  Call TCSActiveModule.EndUpdate
  Call TCSApp.ShowMessageBox( "", "End" )
End Sub

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Пример как желательно обрабатывать ошибки при работе программы, не теряя информацию об ошибке, а обрабатывая ее с показом нормального сообщения пользователю.

Sub Test                   
    'On Error Resume Next - Указывает, что возникновение ошибки выполнения приводит к передаче управления на инструкцию,
    'непосредственно следующую за инструкцией, при выполнении которой возникла ошибка.
    On Error Resume Next 'начало блока обработки ошибок.  
    a = 12/0  ' деление на 0                      
    If Err Then    'если произошла ошибка, то обрабатываем её  
        'Err.Description - описание ошибки
        'Err.Source      - где произошла ошибка       
        Call TCSApp.ShowMessageBox(Err.Source, "Описание: "+Err.Description)        
        Err.Clear ' очищаем информацию об ошибке
    End If                       
    Set b = CreateObject( "NoObject.ThisIsObject" )
    If Err Then    'если произошла ошибка, то обрабатываем её  
        'Err.Description - описание ошибки
        'Err.Source      - где произошла ошибка       
        Call TCSApp.ShowMessageBox(Err.Source, "Описание: "+Err.Description)        
        Err.Clear ' очищаем информацию об ошибке
    End If                       
    On Error goto 0 ' останавливаем обработку ошибок              
End Sub

Пример  как самому сгенерировать ошибку и таким образом прервать выполнение кода (и отлавливать ошибку если нужно)

Function TestRaise(a, b) 
    TestRaise = 0
    If b = 0 Then  
        Call Err.Raise(100, "TestRaise", "Деление на 0 [ "+CStr(a)+"/0 ]") 
    Else  
        TestRaise = a / b    
    End If
End Function
         
Sub Test2 
    'Err.Raise(number[, source, description, helpfile, helpcontext])
    On Error Resume Next 'начало блока обработки ошибок.    
        Call TestRaise(12, 0)                            
        If Err Then    'если произошла ошибка, то обрабатываем её       
            Call TCSApp.ShowMessageBox(Err.Source, "Описание: "+Err.Description)        
            Err.Clear ' очищаем информацию об ошибке
        End If                       
    On Error goto 0 ' останавливаем обработку ошибок              
End Sub

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Выбор ветки дерева по полному пути, и создание если ветка отсутсвует

Function SelectWorkNode( Form, Module, TreePath )
  SelectWorkNode = False
  'Dim Module as ITModule
  On Error Resume Next
  Dim Nodes
  Nodes = Split( TreePath, "\\" )
  Dim Node 'as IDbNode
  Dim CNode 'as IDbNode
  Set Node = Module.DbTree.RootNodes.Item(0)
  For i=LBound(Nodes) to UBound(Nodes)
    Set CNode = Nothing
    For j=0 to Node.Count-1
      Set CNode = Node.Item(j)
      if CNode.Text=Nodes(i) then Exit For
      Set CNode = Nothing
    Next
    If CNode Is Nothing then
      Node.Selected = True
      Module.DbTree.CreateNew
      Module.DbTree.Properties("NODETEXT").AsString = Nodes(i)
      Module.DbTree.SaveChangesEx
      Module.DbTree.CancelChanges
      If Err then
        Call TCSApp.ShowDetailedErrorMessage( "Ошибка при создании классификатора:" & Nodes(i), Err.Description )
        Exit Function
      End If
      Set Node = Module.DbTree.Selected
    Else
      Set Node = CNode
    End If
  Next
  Node.Selected = True
  If Err then
    Call TCSApp.ShowDetailedErrorMessage( "Ошибка при выборе классификатора:" & TreePath, Err.Description )
    Exit Function
  End If
  SelectWorkNode = True
End Function
Спасибо сказали: Дмитрий Гамий1

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Пример бытсрого создания форм ввода с помощью дерева
Быстрое создание формы ввода для заполнения данных пользователем

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Ролик демонстрирующий совместные возможности использования АПИ, Форм ввода, Наборов данных и Интерфейсов пользователя http://csdn.csoft.ru/public/TechnologiCS570Features.avi. Создан для версии 5.7, но информация актуальна, хотя некоторые вещи уже делаются проще.

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Пример работы с цехами в рабочем профиле пользователя через АПИ

Sub TestUserCehaProfile
  if not TCSAPp.Inventory.TcsUserProfile.HasUserProfile then
    Call TCSAPp.ShowErrorMessage( "Выберите профиль пользователя!" )
    Exit Sub
  end if
  CehaIds = TCSApp.Inventory.TcsUserProfile.GetProfilePlantDepartments
  if IsNull(CehaIds) then
    Call TCSAPp.ShowErrorMessage( "В профиле нет цехов!" )
    Exit Sub
  End If

  if IsEmpty(CehaIds) then
    Call TCSAPp.ShowMessageBox( "", "Профиль не имеет ограничений на цеха (по всем цехам)!" )
  Else
    if TCSApp.PlantDepartments.RunModuleForSelect( "Select ceha to check profile", True )>0 then
       For i=0 to TCSApp.PlantDepartments.SelectedRowsCount-1
         if TCSApp.PlantDepartments.GotoSelectedRow(i) then
           Found = False
           for j=LBound(CehaIds) to UBound(CehaIds)
             if TCSApp.PlantDepartments.p_ID.AsInteger=CehaIds(j) then Found = True
           Next
           if Found then
             Call TCSAPp.ShowMessageBox( "", "Профиль <" & TCSApp.PlantDepartments.p_NAME.DisplayText + "> включен в профиль!" )
           Else
             Call TCSAPp.ShowErrorMessage( "Профиль <" & TCSApp.PlantDepartments.p_NAME.DisplayText + "> НЕ включен в профиль!" )
           End If
         End If
       Next
    End If
  End If
End Sub
Спасибо сказали: Дмитрий Гамий1

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Примеры использованяи кастомизации.
Желательно код разместить в модуле test01 (или поправить код)

Sub MyCommand1( ActiveModule, ExecutionParams )
 'Dim ExecutionParams As IExecutionParams
 Call theApp.ShowMessageBox( "MyCommand1", ExecutionParams.CmdParamByName( "A" ) )
 ExecutionParams.ReturnCode = 0
 ExecutionParams.ReturnMessage = "можно еще здесь мессагу показать"
End Sub

Sub TestCM
  Dim Mes'as IMesuriments
  Set Mes = theApp.Mesuriments
  Mes.UserModuleName = Mes.UniqueUserModuleName
  Call theApp.DeleteModuleByUserModuleName( Mes.UserModuleName )

  Dim CM' as ICustomizedModule
  Set CM = Mes
  if Mes.ShowModal( "Test1 (initial module)" ) then:

  CM.CM_ClearAllCustomization   'Очистим все ранее сделанные кастомизации
  Call CM.CM_ShowCommand( False, "Добав*" )  'Скроем команду(ы) и все что с ней связано
  Call CM.CM_ShowMenuItem( False, "Редак*" ) 'Скроем элемент(ы) меню
  Call CM.CM_ShowToolBarItem( False, "Удалить" ) 'Скроем кнопку(ки) панели кнопок
  CM.CM_ApplyCustomization 'Применим сделанные изменения
  if Mes.ShowModal( "Test2 (hide commands)" ) then:

  CM.CM_ClearAllCustomization 'Очистим все ранее сделанные кастомизации
  CM.CM_ApplyCustomization  'Применим сделанные изменения
  if Mes.ShowModal( "Test3 (restore commands)" ) then:

  Call CM.CM_ShowGridColumn( False, "Обозначе*" ) 'Скроем колонку сетки
  Call CM.CM_EnablePopupMenu( False ) 'Запретим вызов меню
  Call CM.CM_EnableShortCuts( False ) 'Запретим горячие клавиши
  Call CM.CM_ShowToolBar( False )  'Скроем панели кнопок
  if Mes.ShowModal( "Test4 (hide column, menu, toolbar, keys)" ) then:

  Call CM.CM_ShowGridColumn( True, "*" ) 'Покажем все колонки
  Call CM.CM_SetGridColumnBackground( RGB( 200, 0, 20 ), "Обоз*" ) 'Установим цвет колонки
  if Mes.ShowModal( "Test5 (color column)" ) then:

  CM.CM_ClearAllCustomization
  CM.CM_AddGridStyle("If GetAsText(""Обозначение"")=""шт"" then StyleColor( ""Обозначение"", RGB( ""Lime"" ) ) End If") 'Раскрасим обозначение штук
  CM.CM_ApplyCustomization
  if Mes.ShowModal( "Test6 (color row)" ) then:

  Call CM.CM_AddUserCommand( "MyCommand1", "Моя команда на скриптах", 1, "test01.MyCommand1", "A=Можно передать параметр свой" )
  CM.CM_ApplyCustomization
  if Mes.ShowModal( "Test7 (run command!" ) then:

  Set Users = theApp.Users
  Users.UserModuleName = Users.UniqueUserModuleName
  Call theApp.DeleteModuleByUserModuleName( Users.UserModuleName )
  Call CM.CM_ShowChildItem( False, "Параметры*" ) ' Скроем зависимый объект
  CM.CM_ApplyCustomization
  Call CM.CM_ClearUserRptChildItems
  Call CM.CM_Add_UserRptChildItemModule( True, "0_MyChildItem", "Мой зависимый объект", 1, Users ) 'Добавим свой зависимый объект (пока только в отчетах работает)
  Call CM.CM_ShowChildModules( True )
  if Mes.ShowModal( "Test8 (test new child item in report!" ) then:

End Sub
Спасибо сказали: Дмитрий Гамий1

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

Немного разьяснений по поводу работы с памятью.

В TechnologiCS АПИ есть три области хранения:

  • локальная

  • глобальная

  • автоматическая.

В локальной все объекты работают в рамках области видимости переменной, и уничтожаются вместе с ней (либо при приcвоении всем ссылкам на этот модуль Nothing).
Если это не глобальная переменная, то как правило никаких действий не требуется (не надо всем объектам присваивать Nothing).
Большинство объектов живут именно так.

В автоматической объекты уничтожатся сами, по требования системы. Как например ссылки на свойства уничтожаются вместе с модулем которому они принадлежат. При попытке работать с ними после вы получите ошибку Object is no longer valid

В глобальной объекты создаются и существуют все время во время работы программы.
Это можно проверить таким образом. Запустим такой код

if theApp.FileTypes.ShowModal( "Test" )>0 Then 
End If

Запустите, в окне как то настройте модуль (перейдите на определенную запись например, выделите записи, фильтр используйте). При следующем запуске модуль останется в том же состоянии.
Это делается для того, чтобы не читать при каждом запуске все справочники, не хранить постоянно все переменные для работы и таскать их с собой (на каждый справочник свою), сохранять последний выбор и настройки.
Как правило так работают все объекты взятые от Application.

К сожалению, при активном использовании в работе объектов типа SingleDoc (которые работают в глобальной области), создается куча объектов и расходуются ресурсы компьютера (часто совершенно излишне).

Для уничтожения таких объектов предназначены методы:

  • UserModuleName (дать модулю глобальное имя и перевести в глобальную область),

  • DeleteModuleByUserModuleName (перевести модуль в локальную область/в старых версияю удалить модуль),

  • ModuleByUserModuleName (получить модуль из глобальной области по имени).

Эти методы могут перевести объект в глобальную область, перевести в локальную область, сделать несколько копий глобальных объектов (с разными настройками например).


Пример иллюстрирующий работу всех областей хранения объектов.

Sub TestObjectScope
 'Dim ft as IFileTypeCommands
 'Dim ftCmd as IFileTypeCommands
 'Create named global module
 Set ft = Nothing
 Set ftCmd = Nothing
 On Error Resume Next
 Set ft = theApp.ModuleByUserModuleName( "MyFileTypes" )
 Set ftCmd = theApp.ModuleByUserModuleName( "MyFileTypeCommands" )
 On error goto 0
 if ft is Nothing then
   Set ft = theApp.FileTypes
   ft.UserModuleName = "MyFileTypes" 
 End If
 Set ft = Nothing

 'Показать глобальную область
 if theApp.FileTypes.ShowModal( "Настройте глобальный модуль <ТИПЫ ФАЙЛОВ> (выделите записи, фильтр и пр.)" )>0 then
 End If
 if theApp.FileTypes.ShowModal( "При последующих обращениях <ТИПЫ ФАЙЛОВ> настройки остались!" )>0 then
 End If
 Set Ch = theApp.FileTypes.Properties( "COMMANDS" ).AsIDispatch
 'Показать локальную область
 if Ch.ShowModal( "Настройте локальный модуль <КОМАНДЫ> (выделите записи, фильтр и пр.)" )>0 then
 End If
 if ftCmd is Nothing then
   Set ftCmd = Ch
   ftCmd.UserModuleName = "MyFileTypeCommands" 
 End If

 Set Ch = theApp.FileTypes.Properties( "COMMANDS" ).AsIDispatch
 if Ch.ShowModal( "При последующих обращениях <КОМАНДЫ> настройки сбросились (так как модуль уничтожен)!" )>0 then
 End If

 Set P = ch.Properties("ID")
 Call theApp.ShowMessageBox( "Показ автоматического удаления 1", "Значение свойства ID=" & P.AsSafeInteger )
 ''Удалиим локальный модуль сами
 Set Ch = Nothing
 On Error Resume Next
 Err.Clear
 Call theApp.ShowMessageBox( "Показ автоматического удаления 2", "Значение свойства ID=" & P.AsSafeInteger ) 'Этот код никогда не сработает из за ошибки
 if Err Then
   Call theApp.ShowDetailedMessage( "Показ автоматического удаления 3", "А объекта уже нет!", Err.Description )
 End if
 On Error Goto 0

 Set ft = theApp.ModuleByUserModuleName( "MyFileTypes" )
 'Покажем глобальный модуль с именем (копия глобального)
 if ft.ShowModal( "Настройте именованный глобальный модуль <ТИПЫ ФАЙЛОВ> по другому (выделите записи, фильтр и пр.)" )>0 then
 End If
 if theApp.FileTypes.ShowModal( "Это глобальный модуль <ТИПЫ ФАЙЛОВ> (все настройки остались!" )>0 then
 End If
 if ft.ShowModal( "Это именованный глобальный модуль <ТИПЫ ФАЙЛОВ> (все свои настройки остались!" )>0 then
 End If

 'Покажем глобальный модуль с именем (сделанный из локального)
 if ftCmd.ShowModal( "Это именованный локальный модуль <КОМАНДЫ>, котороый мы сделали глобальным. Он не уничтожен и все настройки сохранились!" )>0 then
 End If

 'Удалим все глобальные модули если нужно
 if theApp.MessageBoxAskYesNo( "Подтвердите действие", "Удалить все глобальные объекты в этом примере?", False, 2, True )=1 then
   Call theApp.DeleteModuleByUserModuleName( "MyFileTypes" )
   Call theApp.DeleteModuleByUserModuleName( "MyFileTypeCommands" )
   theApp.FileTypes.UserModuleName = theApp.UniqueUserModuleName
   Call theApp.DeleteModuleByUserModuleName( theApp.FileTypes.UserModuleName )
 End If

End Sub
Спасибо сказали: Дмитрий Гамий1

(изменено: Garry, 6 июля 2023 12:00:39)

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

У меня в UserGModule столбец Резерв, тип 6 (float). Значения есть не во всех строках.
Как подсветить ячейки с любым не пустым значением?

не работает

CM.CM_AddGridStyle("If GetAsText(""Резерв"")>0 then : StyleColor(""Резерв"", ""Red"") : end if")

Как изменить цвет текста, а не фона?

Re: Некоторые замечания и советы по использованию TechnologiCS АПИ

https://help.technologics.ru/7.9/TCSHelp/_808.htm FontColor у этой функции.

PS. Сообщения удалю попозже, это сборник рецептов, без лишнего.