PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Вывод информации ListViewGadget


Вывод информации ListViewGadget

Сообщений 1 страница 27 из 27

1

Пишу программу выводящую и принимающую информацию в/из КОМ-порт.
При этом, необходимо выводить лог в окне.
Использую для этого ListViewGadget.
Что бы фокус был на актуальном событии, использую команду SetGadgetState.
Впечатление, что в этот момент система виснет (примерно 5ms), поскольку появляются разрывы в потоке на RS-232.

Как можно это обойти, или вообще по-другому сделать вывод лога?

0

2

Попробуем сместить акценты.
Какие существуют способы вывода лога на экран, что бы фокус был на последнем сообщении?

0

3

Дело не в акцентах.

Из ваших сообщений абсолютно не понятно, чем вам помочь. Если есть потери в работе с внешним оборудованием однозначно нужно думать о разведении на разные потоки работу с оным и работу с экраном.
Что в вашем понимании лог? Какая интенсивность, если большая, то точно стандартные гуёвые системные контролы не годятся. Нужно изобретать своё. Но не придумывать же нам, если вы не рассказываете что вы делаете.
Нужно ли лог ещё куда то сохранять кроме экрана?
Если при рече о потерях упомянуты мили секунды, то причём здесь глаза и фокус на контроле.
Может лог показывать с прореживанием доступным глазу а остальное более детализированно потом для постанализа.

Короче давайте больше деталей.

Отредактировано useful (14.03.2021 17:12:41)

0

4

useful написал(а):

Что в вашем понимании лог?

Я полагаю, то же, что и в Вашем: последовательное протоколирование событий.
А вот как его визуализировать, это как-раз я и спрашиваю.
Я сделал как в подавляющем большинстве видимых мною программах:
В окно "Едитора" последовательно выводятся строки лога, с "подсветкой" последней строки.
При заполнении окна, весь массив строк сдвигается вверх для того, что бы последняя строка была видна (кстати, поэтому использую, ListViewGadget, поскольку в EditorGadget такое проделать не удалось).
Опытным путём установлено, что проблема (пауза в передаче данных через ком-порт) возникает именно при этом "сдвиге".
Если отключить фокусировку на последней строке, и дать им (последним строкам) спокойно "тонуть", то никаких разрывов не возникает.
Работа уже разведена по потокам.

0

5

Gregory написал(а):

Работа уже разведена по потокам.

Прием данных из порта и добавление в ListViewGadget в одном потоке?
Можно из потока приема отправлять данные окну основного потока функцией PostEvent.

0

6

Я серьёзно.
Мы точно можем по разному смотреть на одно и тоже. Лог это протокол для детального сохранения и последующего анализа.
А оперативная визуализация обычно это точно не детальное отображение, особенно при интенсивном потоке, а только показ, что что то происходит и допустим не выбивается или выбивается из приемлемого диапазона каких то характеристик.
Поэтому очень важно, сколько данных и с какой интенсивностью вы пытаетесь переварить, эти данные существенно меньше доступной оперативной памяти или наоборот заведомо её превышают?

0

7

Пётр написал(а):

отправлять данные окну основного потока функцией PostEvent

Пётр, ну я же выше ссылку давал, где мы с Вами это обсуждали.
Всё сделано через буфер, мьютексы и PostEvent.
Запись в окно - в главном потоке.
Всё прекрасно работает, пока не добавляю SetGadgetState на последнюю строку.

useful написал(а):

Лог это протокол для детального сохранения и последующего анализа.

Категорически согласен.
Собственно у меня это и делается: время+байты+расшифровка в текстовой форме.
Как выше говорил, всё прекрасно "переваривается" до добавления SetGadgetState.

0

8

Gregory написал(а):

Всё сделано через буфер, мьютексы и PostEvent.

В теме PostEvent только в моем примере.
В вашем коде этой функции нет.

Gregory написал(а):

Запись в окно - в главном потоке.
Всё прекрасно работает, пока не добавляю SetGadgetState на последнюю строку.

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

0

9

Gregory написал(а):

Категорически согласен.
Собственно у меня это и делается: время+байты+расшифровка в текстовой форме.
Как выше говорил, всё прекрасно "переваривается" до добавления SetGadgetState.

Я спросил
...сколько данных и с какой интенсивностью вы пытаетесь переварить, эти данные существенно меньше доступной оперативной памяти или наоборот заведомо её превышают?

Но вы продолжаете упорствовать, утверждая, что у вас всё прекрасно работает.
А я на 111% уверен в том, что у вас архитектурная проблема в алгоритме.

0

10

Пётр написал(а):

В теме PostEvent только в моем примере.

Так я же написАл там, что изрядно перелопатил код следуя умным советам.
Но там описывалась проблема из-за попытки выводить сообщения в окно из неосновного потока (о чём я был не в курсе).

Пётр написал(а):

Покажите фрагменты кода

ОК.
Но надо время, что б наковырять.

useful написал(а):

Но вы продолжаете упорствовать

Отнюдь.
Я просто не очень понимаю, что Вы хотите увидеть (или услышать)?
Я уже неоднократно говорил, что проблема появляется при заполнении окна, и не зависит от количества данных.
Что три строки, что тысяча.
Интервалы вывода строк получаются, в зависимости от длинны пакета, от 1 до 3 мс.

useful написал(а):

у вас архитектурная проблема в алгоритме

Так я и спрашиваю, кто реализовывал работоспособный код?

0

11

Gregory
1. Я недавно пробовал инфу читать из потока stdout. Мне дали пример бинарного чтения, он всегда обрезал концовку, из-за того что читал блоками определённого размера, вроде как на следующем шаге цикла должен был дочитать что не дочитал, но в итоге данные уже заполнялись новыми, то есть надо было всё читать, видимо передаётся флаг, что данные прочитаны и буфер заполняется новыми данными, иначе как бы он знал когда данные поступаю, когда прочитаны. Думаю функция запроса наличия данных AvailableProgramOutput это просто проверка битового флага и размера данных, и она может быть выполнена условно миллион раз в сек без каких либо тормозов.
2. Данные читал размером по 1 кб и счётчик показывал 1500 вызовов за какие то несколько миллисекунд, я увеличил буфер до 10240 байт и у меня было уже 150 вызовов. Ну и тут предположение, если прога читает построчно (режим другой), то число вызовов ещё больше, так как одна строка это допустим 100 байт, даже не 1000. Отсюда вывод, если вы в потоке чтения ещё и обслуживаете окно, то явно будут пропущены данные. Надо читать данные в отдельном потоке.
3. Запустить чтение в отдельный поток и соорудить аналог stdout в своей программе между данными и окном. Для этого наполняем какую либо переменную данными и тоже ставим флаг что первые данные получены. Цикл окна копирует данные и включает флаг, что данные прочитаны, функция чтения данных в цикле так же считывает это флаг и если получила сигнал что окно считало данные, то обнуляет данные и наполняет новыми. То есть процесс изменения флага с 0 на 1 и обратно никак не будет тормозить функцию чтения stdout.

Отредактировано AZJIO (16.03.2021 01:18:12)

0

12

И в третий раз забросил старик невод в воду.... :flag:
...сколько данных и с какой интенсивностью вы пытаетесь переварить, эти данные существенно меньше доступной оперативной памяти или наоборот заведомо её превышают?

Gregory написал(а):

Интервалы вывода строк получаются, в зависимости от длинны пакета, от 1 до 3 мс.

Я правильно вас понял, что вы хотите и главное считаете себя способным осмысленно глазами анализировать от 333 до 1000 строк в секунду, но у вас не получается их показать? o.O

Когда я говорю о ошибке в алгоритме, я имею ввиду то, что при правильном алгоритме проблема ГУИ не должна влиять на потери информации на приёме, а вы ведь об этом заявляли в самом начале.

Gregory написал(а):

...поскольку появляются разрывы в потоке на RS-232.

Отредактировано useful (16.03.2021 10:57:45)

0

13

Обещанные куски кода:

Код:

Procedure.a WriteToBufferLogger(TempFullStringProced$)

   Protected TempL.l

      LockMutex(MutexWriteLogger)

        If CounterBufferLoggerWrite <> CounterBufferLoggerRead Or VolBufferLogger = 0

            LoggerWriteArray$(CounterBufferLoggerWrite) = TempFullStringProced$
             CounterBufferLoggerWrite = (CounterBufferLoggerWrite + 1) % #CounterLoggerBufferMax
             VolBufferLogger = 1

        Else
            TempL = (CounterBufferLoggerWrite + (#CounterLoggerBufferMax - 1)) % #CounterLoggerBufferMax
            LoggerWriteArray$(TempL) = "  !!! Error !!! Buffer is full !!!"

        EndIf

      UnlockMutex(MutexWriteLogger)

      PostEvent(#CreateMessageForLogger)



EndProcedure 




Procedure.a WriteToLogger()

      LockMutex(MutexWriteLogger)

        If VolBufferLogger > 0

                 AddGadgetItem (#ViewEditor, -1, LoggerWriteArray$(CounterBufferLoggerRead))
                 SetGadgetState (#ViewEditor, ValStringInLogger)
                 ValStringInLogger = ValStringInLogger + 1

                 CounterBufferLoggerRead = (CounterBufferLoggerRead + 1) % #CounterLoggerBufferMax

                 If CounterBufferLoggerRead = CounterBufferLoggerWrite
                      VolBufferLogger = 0
                 Else
                 EndIf

        Else
        EndIf

      UnlockMutex(MutexWriteLogger)


EndProcedure 







Procedure.a WriteDataToCycleBuffer (Array TempCreateArayP.a(1), ValueByteP.l)  ; 

    Protected TempProcedur.l = 0
    Protected TempVolWriteToCycleBuffer.a = 0

              For TempProcedur = 0 To ValueByteP - 1

                  Repeat
                    TempVolWriteToCycleBuffer = 0

                      LockMutex(MutexCycleBuffer)

                     If CounterCycleBufferSendRead <> CounterCycleBufferSendWrite Or VolCycleBufferComPort = 0


                         CycleBufferSendComPort(CounterCycleBufferSendWrite) = TempCreateArayP(TempProcedur)
                         CounterCycleBufferSendWrite = (CounterCycleBufferSendWrite + 1) % #CounterCycleBufferSendMax
                         VolCycleBufferComPort = 1
                         UnlockMutex(MutexCycleBuffer)

                         TempVolWriteToCycleBuffer = 1

                     Else
                        UnlockMutex(MutexCycleBuffer)
                        Delay(200)


                     EndIf

                  Until TempVolWriteToCycleBuffer = 1


              Next



EndProcedure









; ===== Поток в котором данные из циклического буфера передаются в КОМ-порт ======




;--------------- View Output NoFull


      If VolLoggerWriteOutputNoFull = 1


          If StringSendMessage$ <> StringSendMessageNoFull$
                StringSendMessageNoFull$ = StringSendMessage$

                  TempFullString$ = TimeSendString$ + " TX-->" + StringSendMessage$

                    For Temp = ValBytesInMessageRealOut To ValBytesInMessageOut-1
                          TempFullString$ = TempFullString$ + "  --"
                    Next

                    For Temp = ValBytesInMessageOut To 3-1
                          TempFullString$ = TempFullString$ + "    "
                    Next

                       TempFullString$ = TempFullString$ + "     ; NoFull!!!  "

                       Temp$ = NameMessage(ArrayByteSend(0))
                       TempFullString$ = TempFullString$ + Temp$


                       WriteToBufferLogger(TempFullString$)

          Else
          EndIf

           VolLoggerWriteOutputNoFull = 0


      ElseIf VolLoggerWriteOutputNoFull = 0
      Else
         MessageRequester(FatalError00$, FatalErrorMes01_00$  + SetStringOutSys$ + "Code:  50061" + SetStringOutSys$ + FatalErrorMes01_01$)
         StopComPort()
         End
      EndIf












; ===== Основной поток ======


            If OpenWindow(#Window_ProgressBar, WindowX(#Window_Main) + WindowWidth(#Window_Main)/2-#Width_WindowProgressBar/2, WindowY(#Window_Main) + WindowHeight(#Window_Main)/2-#Height_WindowProgressBar/2-80, #Width_WindowProgressBar, #Height_WindowProgressBar, WindowsProgress2$, #PB_Window_Tool, WindowID(#Window_Main))
                DisableWindow(#Window_Main,1)

               If ProgressBarGadget(#ProgressBarIndSysex, 5, 5, #Width_WindowProgressBar-10, #Height_WindowProgressBar-10, 0, SizeDumpFileOrig)
                  DisableWindow(#Window_ProgressBar,1)

                      For TempL = 0 To SizeDumpFileOrig - 1

                        If TempL & %1111111111 = 0
                           SetGadgetState (#ProgressBarIndSysex, TempL)     ; Info to ProgressBar
                        Else
                        EndIf

                        TempArrayCreateMidiCommand(0) = SysExDump(TempL)
                        WriteDataToCycleBuffer (TempArrayCreateMidiCommand(), 1)

                        If TempL & #MaskWindowEventForCOM = 0
                            Event = WindowEvent()
                            If Event = #CreateMessageForLogger
                                 WriteToLogger ()
                            Else
                            EndIf
                        Else
                        EndIf


                      Next


                      LockMutex (MutexEndSysexFile)
                         EndSysexFile = 1
                      UnlockMutex (MutexEndSysexFile)


                      Repeat
                          Event = WindowEvent()

                          If Event

                            If Event = #CreateMessageForLogger
                                 WriteToLogger ()
                            Else
                            EndIf

                          Else
                            Delay(1)
                          EndIf

                      Until Event = #EndTransferSysEx



                     CloseWindow(#Window_ProgressBar)

                     DisableWindow(#Window_Main,0)
                     UseGadgetList(OldGadgetList)

               Else
                   MessageRequester(FatalError00$, FatalErrorMes01_00$  + SetStringOutSys$ + "Code:50????" + SetStringOutSys$ + FatalErrorMes01_01$) ; 
                   End
               EndIf

            Else                     ; Window of Status-bar
                MessageRequester(FatalError00$, FatalErrorMes01_00$  + SetStringOutSys$ + "Code:50????" + SetStringOutSys$ + FatalErrorMes01_01$) ; 
                End
            EndIf
AZJIO написал(а):

Мне дали пример бинарного чтения

Честно говоря, "с хода" плохо понял, о чём это?
Надо вдумчиво почитать.
Но всё-равно, спасибо. :flirt:

useful написал(а):

И в третий раз забросил

Gregory написал(а):

проблема появляется при заполнении окна, и не зависит от количества данных. Что три строки, что тысяча.

Вы хотите конкретных цифр?
ОК.
Данные: от 10 байт до 1,5Мб.
ОЗУ: 1Гб.

useful написал(а):

Я правильно вас понял

Складывается ощущение, что нет.
С выводом строк была проблема по недоитию (см. выше), но с подсказки Петра, она устранена.
Мне всё-равно сколько будут "сыпаться" события в окне логера после окончания передачи/приёма (хоть час, условно, разумеется, если понадобится, я потом пролистаю и посмотрю, или вообще в файл сохраню), мне важно, что бы они не тормозили поток в котором данные в КОМ-порт передаются.

useful написал(а):

проблема ГУИ не должна влиять на потери информации

Вот именно, с тем лишь уточнением, что у меня не потери, а паузы.

0

14

Gregory
мы говорим о разном, мой пример даже не близко, ваш код помог понять что у вас другое.

0

15

AZJIO написал(а):

ваш код помог понять что у вас другое

Ясно, но всё-равно, спасибо. :yep:

0

16

Gregory написал(а):

Обещанные куски кода

В WriteToLogger() захватывается мьютекс и производится работа с гаджетами. Если в это время будет вызвана процедура WriteToBufferLogger, в ней будет ожидаться освобождение мьютекса.
Попробуйте этот вариант

Код:
Procedure.a WriteToLogger()
  Protected s.s="", Pos=-1
  
  LockMutex(MutexWriteLogger)
  
  If VolBufferLogger > 0
    s = LoggerWriteArray$(CounterBufferLoggerRead)
    Pos = ValStringInLogger
    
    ValStringInLogger = ValStringInLogger + 1
    
    CounterBufferLoggerRead = (CounterBufferLoggerRead + 1) % #CounterLoggerBufferMax
    
    If CounterBufferLoggerRead = CounterBufferLoggerWrite
      VolBufferLogger = 0
    Else
    EndIf
    
  Else
  EndIf
  
  UnlockMutex(MutexWriteLogger)
  
  If s And Pos>=0
    AddGadgetItem (#ViewEditor, Pos, s)
    SetGadgetState (#ViewEditor, Pos)
  EndIf
  
EndProcedure 

PS.
Код можно было бы значительно упростить если использовать двусвязный список вместо массива в режиме кольцевого буфера.
А начиная с версии 5.72 можно просто передавать строку.

Код:
Enumeration 
  #Window_0
  #Editor
EndEnumeration 

Enumeration #PB_EventType_FirstCustomValue
  #EventType_SendText
EndEnumeration

Procedure Progr(*x)
  Protected *s.String
  
  For a=0 To 100000
    *s = AllocateStructure(String)
    If *s
      *s\s = "Событие № "+a
      If PostEvent(#PB_Event_Gadget, #Window_0, #Editor, #EventType_SendText, *s) = 0
        FreeStructure(*s) ; Не удалось отправить сообщение, поэтому освобождаем память.
      EndIf
    EndIf
    Delay(10)
  Next    
EndProcedure  


OpenWindow(#Window_0, 100, 150, 400, 200, "Работа в потоке",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
EditorGadget(#Editor, 0,0, 400, 200)

ThreadID=CreateThread(@Progr(), 0)

Repeat 
  Event = WaitWindowEvent() 
  
  If Event = #PB_Event_Gadget   
    
    Select EventGadget() 
      Case #Editor
        If EventType() = #EventType_SendText
          *s.String = EventData()
          If *s
            AddGadgetItem(#Editor, -1, *s\s)
            SendMessage_(GadgetID(#Editor), #EM_LINESCROLL, 0, 1)
            FreeStructure(*s)
          EndIf
        EndIf
    EndSelect 
    
  EndIf
  
Until Event = #PB_Event_CloseWindow

0

17

Пётр написал(а):

захватывается мьютекс и производится работа с гаджетами

Т.е. Вы хотите сказать, что происходит опустошение буфера, и надо сперва копировать данные во временные переменные, освобождать мьютекс, а только потом выводить в окно?

Пётр написал(а):

с версии 5.72

У меня 5.11, увы. :rolleyes:

0

18

Если поток захватил мьютекс, остальные потоки при попытке захвата, будут ждать освобождения. Ожидание производится внутри функции LockMutex().

0

19

Пётр написал(а):

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

Пётр!
Я в курсе.

0

20

Gregory написал(а):

Я в курсе.

Это было объяснение почему происходит

Gregory написал(а):

Впечатление, что в этот момент система виснет (примерно 5ms)

Вот поэтому и виснет, потому что мьютекс занят пока гаждет не обновится.

0

21

Пётр написал(а):

Попробуйте этот вариант

Попробовал. Те же коки (с SetGadgetState - разрывы, убираю - всё нормально).
Потом решил попробовать на ИксПи (до этого был Сэвэн).
Тут оказалось всё нормально.
И как это понимать? :whistle:

0

22

С ИксПи тоже оказались проблемы (правда при очень больших объёмах), поэтому сделал запись новых строк в логер сразу небольшими пачками, а главное фокусировку сделал как вызов по таймеру.
Получилось несколько дёргано, но разрывы в передаче практически ликвидировались.

0

23

Gregory написал(а):

Попробовал. Те же коки (с SetGadgetState - разрывы, убираю - всё нормально).

может она каждый раз ведёт отсчёт от нуля? отсюда и тормоз, элементов много, цикл большой
попробовать напрямую SendMessage_() использовать

0

24

newJS написал(а):

она каждый раз ведёт отсчёт от нуля?

Я тоже пришёл к такому заключению. :-)

newJS написал(а):

SendMessage_()

А с Линухом как быть?

0

25

И всё-таки, существует ли какой-то способ, как ускорить вывод информации?
Сейчас необходимо визуализировать дамп ПЗУ.
Так вот, небольшие дампы ещё куда ни шло, а вот вывод 4Mb дампа занимает более 10 минут.
Ради чистоты эксперимента, убрал все преобразования данных из дампа и тупо сделал вывод одной и той же строки.
Время не изменилось.

0

26

Можно, если использовать так называемый виртуальный ListView.

Код:
#ItemCount = 1000000

#LVSICF_NOINVALIDATEALL = 1
#LVSICF_NOSCROLL = 2 
#LVN_ODCACHEHINT = #LVN_FIRST - 13


Global Dim myItems.s(#ItemCount,1) 
Global Dim CheckFlag(#ItemCount)

Procedure WinCallback(hwnd, msg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents 
  Select msg 
    Case #WM_NOTIFY                             
      *nmh.NMHDR = lParam 
      Select *nmh\code
          
        Case #LVN_ODCACHEHINT 
          result = 0
          
        Case #LVN_ODFINDITEM
          result = -1   
          
        Case #LVN_GETDISPINFO 
          *nmlvd.NMLVDISPINFO = lParam
          *nmlvd\item\mask & (#LVIF_TEXT|#LVIF_STATE)  ;|#LVIF_IMAGE
          *nmlvd\item\stateMask = #LVIS_STATEIMAGEMASK
          *nmlvd\item\pszText = @myItems(*nmlvd\item\iItem,*nmlvd\item\iSubItem)
          
          Select CheckFlag(*nmlvd\item\iItem)
            Case 0
              *nmlvd\item\state = 1<<12               
            Case 1
              *nmlvd\item\state = 2<<12
          EndSelect
          
        Case #NM_CLICK
          *nmlv.NM_LISTVIEW = lParam
          If *nmlv\iSubItem = 0 And *nmlv\iItem >= 0
            CheckFlag(*nmlv\iItem) = CheckFlag(*nmlv\iItem) ! 1
            SendMessage_(GadgetID(0), #LVM_REDRAWITEMS ,*nmlv\iItem,*nmlv\iItem)
          EndIf
          
      EndSelect      
      
  EndSelect 
  ProcedureReturn result 
EndProcedure 

OpenWindow(0, 0, 0, 640, 300, "Virtual ListIconGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
SetWindowCallback(@WinCallback())
ListIconGadget(0,10,10,620,280,"ID",100,#LVS_OWNERDATA|#PB_ListIcon_CheckBoxes| #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect)
SetGadgetColor(0,#PB_Gadget_BackColor, $E1FEFD) 
SendMessage_(GadgetID(0), #LVM_SETITEMCOUNT, #ItemCount, #LVSICF_NOINVALIDATEALL|#LVSICF_NOSCROLL) 
AddGadgetColumn(0,2,"Name",100) 
For i=0 To #ItemCount 
  myItems(i,0) = Str(i) 
  myItems(i,1) = "Name "+Str(i) 
Next i 


Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow

Миллион строк за 2 секунды.

0

27

Пётр написал(а):

так называемый виртуальный ListView

Так понимаю, это только под Винду?
А под Линь?

0


Вы здесь » PureBasic - форум » PureBasic для Windows » Вывод информации ListViewGadget