通过实例理解 .debug_info section

本文解析了通过GCC编译器生成的debug_info.c文件的调试信息。详细介绍了.debug_aranges、.debug_info、.debug_abbrev、.debug_line及.debug_str等节内容,并解释了这些调试信息如何映射到源代码中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Use the following debug_info.c file:

#include<stdio.h>

void func(int arg)
{
    int i = 0;
    int local = arg +42;
    while (i<local)
    {
        printf("i = %d\n", i++);
    }
}

int main()
{
    func(23);
    return (0);
}

Compile debug_info.c to debug_info using -g:  
$ gcc -g -o debug_info debug_info.c

Show the sections .debug_*:

$ objdump -s debug_info|grep "section .debug_" -A 15
Contents of section .debug_aranges:
 0000 2c000000 02000000 00000800 00000000  ,...............
 0010 26054000 00000000 57000000 00000000  &.@.....W.......
 0020 00000000 00000000 00000000 00000000  ................
Contents of section .debug_info:
 0000 d3000000 04000000 00000801 66000000  ............f...
 0010 0c590000 00250000 00260540 00000000  .Y...%...&.@....
 0020 00570000 00000000 00000000 00020807  .W..............
 0030 13000000 0201083d 00000002 02070000  .......=........
 0040 00000204 07180000 00020106 3f000000  ............?...
 0050 020205c1 00000003 0405696e 74000208  ..........int...
 0060 05500000 00020807 cb000000 02010646  .P.............F
 0070 00000004 bc000000 01032605 40000000  ..........&.@...
 0080 00004200 00000000 0000019c b9000000  ..B.............
 0090 05617267 00010357 00000002 915c0669  .arg...W.....\.i
 00a0 00010557 00000002 916807b6 00000001  ...W.....h......
 00b0 06570000 0002916c 00084b00 0000010d  .W.....l..K.....
 00c0 57000000 68054000 00000000 15000000  W...h.@.........
 00d0 00000000 019c00                      .......         
Contents of section .debug_abbrev:
 0000 01110125 0e130b03 0e1b0e11 01120710  ...%............
 0010 17000002 24000b0b 3e0b030e 00000324  ....$...>......$
 0020 000b0b3e 0b030800 00042e01 3f19030e  ...>........?...
 0030 3a0b3b0b 27191101 12074018 96421901  :.;.'.....@..B..
 0040 13000005 05000308 3a0b3b0b 49130218  ........:.;.I...
 0050 00000634 0003083a 0b3b0b49 13021800  ...4...:.;.I....
 0060 00073400 030e3a0b 3b0b4913 02180000  ..4...:.;.I.....
 0070 082e003f 19030e3a 0b3b0b49 13110112  ...?...:.;.I....
 0080 07401896 42190000 00                 .@..B....       
Contents of section .debug_line:
 0000 45000000 02002300 00000101 fb0e0d00  E.....#.........
 0010 01010101 00000001 00000100 64656275  ............debu
 0020 675f696e 666f2e63 00000000 00000902  g_info.c........
 0030 26054000 00000000 15ad7591 30088e86  &.@.......u.0...
 0040 3f4b9f59 02020001 01                 ?K.Y.....       
Contents of section .debug_str:
 0000 73686f72 7420756e 7369676e 65642069  short unsigned i
 0010 6e74006c 6f6e6720 756e7369 676e6564  nt.long unsigned
 0020 20696e74 002f686f 6d652f61 6c69656e   int./home/alien
 0030 74656b2f 74657374 64697231 00756e73  tek/testdir1.uns
 0040 69676e65 64206368 6172006d 61696e00  igned char.main.
 0050 6c6f6e67 20696e74 00646562 75675f69  long int.debug_i
 0060 6e666f2e 6300474e 55204331 3120352e  nfo.c.GNU C11 5.
 0070 342e3020 32303136 30363039 202d6d74  4.0 20160609 -mt
 0080 756e653d 67656e65 72696320 2d6d6172  une=generic -mar
 0090 63683d78 38362d36 34202d67 202d6673  ch=x86-64 -g -fs
 00a0 7461636b 2d70726f 74656374 6f722d73  tack-protector-s
 00b0 74726f6e 67006c6f 63616c00 66756e63  trong.local.func
 00c0 0073686f 72742069 6e740073 697a6574  .short int.sizet
 00d0 79706500                             ype.      


 Resolve section .debug_aranges:
 0000 2c000000 02000000 00000800 00000000  ,...............
 0010 26054000 00000000 57000000 00000000  &.@.....W.......
 0020 00000000 00000000 00000000 00000000  ................

 (Ref DWARF4 6.1.2 p 107)
 2c000000: section length 0x2c, not include length field
 0200:     version 2
 00000000: debug_info_offset 0
 08:       address_size, 8 bytes of an address
 00:       segment_size 0
 00000000: segment sector 0
 26054000 00000000: begining address 400526
 57000000 00000000: range 57
 00000000 00000000 00000000 00000000: terminate


 Resolve section .debug_info:
 0000 d3000000 04000000 00000801 66000000  ............f...
 0010 0c590000 00250000 00260540 00000000  .Y...%...&.@....
 0020 00570000 00000000 00000000 00020807  .W..............
     

(see DWARF4 7.5.1 p 143)
  d3000000: section length 0xd3, not include length field
  0400:     version 4
  0000 0000: debug_abbrev_offset 0
  08:       address size 8 bytes
  01:       abbrev No 1

 format of abbrev No 1
 (see DWARF4 p 154)
 Contents of section .debug_abbrev:
 0000 01110125 0e130b03 0e1b0e11 01120710  
 0010 17000002
   01: abbreviation code 1
   11:    DW_TAG_compile_unit (Figure 18, p 151)
   01:    DW_CHILDREN_yes (Figure 19, p154)
   25:    DW_AT_producer,(Figure 20, p155)
      0e: DW_FORM_strp, string (p160)
   13 0b: DW_AT_language; DW_Form_data1, constant
   03 0e: DW_AT_name; DW_FORM_strp, string
   1b 0e: DW_AT_dir; DW_FORM_strp, string
   11 01: DW_AT_low_pc; address
   12 07: DW_AT_high_pc; DW_FORM_data8
   10 17: DW_AT_stmt_list; DW_FORM_sec_offset
   00 00: terminate

so the format of abbrev No 1 include fields:
   DW_AT_producer, DW_AT_language, DW_AT_name, DW_AT_dir, DW_AT_low_pc, DW_AT_high_pc, DW_AT_stmt_list

Return to the section .debug_info
  66000000: producer, offset 0x66 in section debug_str,
      0060 ........ ..00474e 55204331 3120352e        GNU C11 5.
      0070 342e3020 32303136 30363039 202d6d74  4.0 20160609 -mt
      0080 756e653d 67656e65 72696320 2d6d6172  une=generic -mar
      0090 63683d78 38362d36 34202d67 202d6673  ch=x86-64 -g -fs
      00a0 7461636b 2d70726f 74656374 6f722d73  tack-protector-s
      00b0 74726f6e 6700....                    trong.
  0c:      DW_AT_language, 12 (ANSI c99)  
  59000000: DW_AT_name, offset 0x59 in section debug_str, debug_info.c   
      0050 ........ ........ 00646562 75675f69            debug_i
      0060 6e666f2e 6300.... ........ ........  nfo.c
  25000000: DW_AT_dir, offset 0x25 in section debug_str, /home/alientek/testdir1
  260540 00000000 00: DW_AT_low_pc, 0x400526
  570000 00000000 00: DW_AT_high_pc, 0x57
  00000000: DW_AT_stmt_list 0

0020 ........ ........ ........ ..020807  .W..............
0030 13000000 0201083d 00000002 02070000  .......=........
02: abbrev No 2
format of abbrev No 2
 Contents of section .debug_abbrev:
 0010 ......02 24000b0b 3e0b030e 0000....
   02:    abbreviation code 2
   24:    DW_TAG_base_type
   00:    DW_CHILDREN_no
   0b 0b: DW_AT_byte_size; constant
   3e 0b: DW_AT_encoding; constant
   03 0e: DW_AT_name; DW_FORM_strp, string
Return to the section .debug_info
 08: DW_AT_byte_size   8
 07: DW_AT_encoding    7    (unsigned)
 13000000: DW_AT_name, offset 0x13 in section debug_str, long unsigned int
      0010 ....006c 6f6e6720 756e7369 676e6564  
      0020 20696e74 00......   


Resolve section .debug_line:
see DWARF4 6.2
 0000 45000000 02002300 00000101 fb0e0d00  E.....#.........
 0010 01010101 00000001 00000100 64656275  ............debu
 0020 675f696e 666f2e63 00000000 00000902  g_info.c........
 0030 26054000 00000000 15ad7591 30088e86  &.@.......u.0...
 0040 3f4b9f59 02020001 01                 ?K.Y.....  

45000000: section length 0x45, not include length field
0200:     version 2
23000000: header length 0x45, not include length field
01:       mimimum_instruction_length, 1 bytes
01:       default_is_stmt 1
fb:       line_base -5
0e:       line_range 14
0d:       opcode_base 13
00 01010101 00000001 000001: standard_opcode_lengths 0, 1, 1, 1, 1, 0 ,0, 0, 1, 0, 0, 1
00 64656275 675f696e 666f2e63 00: file name, debug_info.c
000000 00: Padding
00 09 02: Extended code 02, DW_LNE_set_address, length 09  
26054000 00000000: set address 0x400526

desired line increment =(opcode - opcode_base) % line_range + line_base
operation advance = (opcode - opcode_base) / line_range
line: line number in source file, line += line increment
operation, machine instruction address, operation += operation adv  

15: Special opcode 8 (0x15 -0x0d = 8), line increment 3, operation advance 0,
    advance Address by 0 to 0x400526 and Line by 3 to 4
ad: Special opcode 160: advance Address by 11 to 0x400531 and Line by 1 to 5
75: Special opcode 104: advance Address by 7 to 0x400538 and Line by 1 to 6
91: Special opcode 132: advance Address by 9 to 0x400541 and Line by 1 to 7
30: Special opcode 35: advance Address by 2 to 0x400543 and Line by 2 to 9
08: Advance PC by constant 17 to 0x400554 (corresponding to Special opcode 255, p120)
8e: Special opcode 129: advance Address by 9 to 0x40055d and Line by -2 to 7
......
0202: DW_LNS_advance_pc 2, Advance PC by 2 to 0x40057d (p119)
000101: Extended code 01, DW_LNE_end_sequence, length 01
 

void func(int arg)
{
  400526:    55                       push   %rbp
  400527:    48 89 e5                 mov    %rsp,%rbp
  40052a:    48 83 ec 20              sub    $0x20,%rsp
  40052e:    89 7d ec                 mov    %edi,-0x14(%rbp)
    int i = 0;  //line 5
  400531:    c7 45 f8 00 00 00 00     movl   $0x0,-0x8(%rbp)
    int local = arg +42;  // line 6
  400538:    8b 45 ec                 mov    -0x14(%rbp),%eax
  40053b:    83 c0 2a                 add    $0x2a,%eax
  40053e:    89 45 fc                 mov    %eax,-0x4(%rbp)
    while (i<local)       // line 7
  400541:    eb 1a                    jmp    40055d <func+0x37>
    {
        printf("i = %d\n", i++);   //line 9
  400543:    8b 45 f8                 mov    -0x8(%rbp),%eax
  400546:    8d 50 01                 lea    0x1(%rax),%edx
  400549:    89 55 f8                 mov    %edx,-0x8(%rbp)
  40054c:    89 c6                    mov    %eax,%esi
  40054e:    bf 04 06 40 00           mov    $0x400604,%edi
  400553:    b8 00 00 00 00           mov    $0x0,%eax
  400558:    e8 a3 fe ff ff           callq  400400 <printf@plt>

void func(int arg)
{
    int i = 0;
    int local = arg +42;
    while (i<local)    //line 7
  40055d:    8b 45 f8                 mov    -0x8(%rbp),%eax
  400560:    3b 45 fc                 cmp    -0x4(%rbp),%eax
  400563:    7c de                    jl     400543 <func+0x1d>
    {
        printf("i = %d\n", i++);
    }
}    //line 11
  400565:    90                       nop
  400566:    c9                       leaveq
  400567:    c3                       retq   

0000000000400568 <main>:

int main()
{    //line 14
  400568:    55                       push   %rbp
  400569:    48 89 e5                 mov    %rsp,%rbp
    func(23);  //line 15
  40056c:    bf 17 00 00 00           mov    $0x17,%edi
  400571:    e8 b0 ff ff ff           callq  400526 <func>
    return (0);    //line 16
  400576:    b8 00 00 00 00           mov    $0x0,%eax
  40057b:    5d                       pop    %rbp
  40057c:    c3                       retq   
  40057d:    0f 1f 00                 nopl   (%rax)


$ objdump -g debug_info

debug_info:     文件格式 elf64-x86-64
 
.debug_aranges 节的内容:

  长度:                    44
  版本:                    2
  .debug_info 节中的偏移量:  0x0
  指针大小:                 8
  节区大小:                 0

    地址               长度
    0000000000400526 0000000000000057
    0000000000000000 0000000000000000

.debug_info 节的内容:

  编译单元 @ 偏移 0x0:
   长度:        0xd3 (32-bit)
   版本:        4
   缩写偏移量:    0x0
   指针大小:    8
 <0><b>:缩写编号:1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (间接字串,偏移量:0x66): GNU C11 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong
    <10>   DW_AT_language    : 12    (ANSI C99)
    <11>   DW_AT_name        : (间接字串,偏移量:0x59): debug_info.c
    <15>   DW_AT_comp_dir    : (间接字串,偏移量:0x25): /home/alientek/testdir1
    <19>   DW_AT_low_pc      : 0x400526
    <21>   DW_AT_high_pc     : 0x57
    <29>   DW_AT_stmt_list   : 0x0
 <1><2d>:缩写编号:2 (DW_TAG_base_type)
    <2e>   DW_AT_byte_size   : 8
    <2f>   DW_AT_encoding    : 7    (unsigned)
    <30>   DW_AT_name        : (间接字串,偏移量:0x13): long unsigned int
 <1><34>:缩写编号:2 (DW_TAG_base_type)
    <35>   DW_AT_byte_size   : 1
    <36>   DW_AT_encoding    : 8    (unsigned char)
    <37>   DW_AT_name        : (间接字串,偏移量:0x3d): unsigned char
 <1><3b>:缩写编号:2 (DW_TAG_base_type)
    <3c>   DW_AT_byte_size   : 2
    <3d>   DW_AT_encoding    : 7    (unsigned)
    <3e>   DW_AT_name        : (间接字串,偏移量:0x0): short unsigned int
 <1><42>:缩写编号:2 (DW_TAG_base_type)
    <43>   DW_AT_byte_size   : 4
    <44>   DW_AT_encoding    : 7    (unsigned)
    <45>   DW_AT_name        : (间接字串,偏移量:0x18): unsigned int
 <1><49>:缩写编号:2 (DW_TAG_base_type)
    <4a>   DW_AT_byte_size   : 1
    <4b>   DW_AT_encoding    : 6    (signed char)
    <4c>   DW_AT_name        : (间接字串,偏移量:0x3f): signed char
 <1><50>:缩写编号:2 (DW_TAG_base_type)
    <51>   DW_AT_byte_size   : 2
    <52>   DW_AT_encoding    : 5    (signed)
    <53>   DW_AT_name        : (间接字串,偏移量:0xc1): short int
 <1><57>:缩写编号:3 (DW_TAG_base_type)
    <58>   DW_AT_byte_size   : 4
    <59>   DW_AT_encoding    : 5    (signed)
    <5a>   DW_AT_name        : int
 <1><5e>:缩写编号:2 (DW_TAG_base_type)
    <5f>   DW_AT_byte_size   : 8
    <60>   DW_AT_encoding    : 5    (signed)
    <61>   DW_AT_name        : (间接字串,偏移量:0x50): long int
 <1><65>:缩写编号:2 (DW_TAG_base_type)
    <66>   DW_AT_byte_size   : 8
    <67>   DW_AT_encoding    : 7    (unsigned)
    <68>   DW_AT_name        : (间接字串,偏移量:0xcb): sizetype
 <1><6c>:缩写编号:2 (DW_TAG_base_type)
    <6d>   DW_AT_byte_size   : 1
    <6e>   DW_AT_encoding    : 6    (signed char)
    <6f>   DW_AT_name        : (间接字串,偏移量:0x46): char
 <1><73>:缩写编号:4 (DW_TAG_subprogram)
    <74>   DW_AT_external    : 1
    <74>   DW_AT_name        : (间接字串,偏移量:0xbc): func
    <78>   DW_AT_decl_file   : 1
    <79>   DW_AT_decl_line   : 3
    <7a>   DW_AT_prototyped  : 1
    <7a>   DW_AT_low_pc      : 0x400526
    <82>   DW_AT_high_pc     : 0x42
    <8a>   DW_AT_frame_base  : 1 字节区块: 9c     (DW_OP_call_frame_cfa)
    <8c>   DW_AT_GNU_all_tail_call_sites: 1
    <8c>   DW_AT_sibling     : <0xb9>
 <2><90>:缩写编号:5 (DW_TAG_formal_parameter)
    <91>   DW_AT_name        : arg
    <95>   DW_AT_decl_file   : 1
    <96>   DW_AT_decl_line   : 3
    <97>   DW_AT_type        : <0x57>
    <9b>   DW_AT_location    : 2 字节区块: 91 5c     (DW_OP_fbreg: -36)
 <2><9e>:缩写编号:6 (DW_TAG_variable)
    <9f>   DW_AT_name        : i
    <a1>   DW_AT_decl_file   : 1
    <a2>   DW_AT_decl_line   : 5
    <a3>   DW_AT_type        : <0x57>
    <a7>   DW_AT_location    : 2 字节区块: 91 68     (DW_OP_fbreg: -24)
 <2><aa>:缩写编号:7 (DW_TAG_variable)
    <ab>   DW_AT_name        : (间接字串,偏移量:0xb6): local
    <af>   DW_AT_decl_file   : 1
    <b0>   DW_AT_decl_line   : 6
    <b1>   DW_AT_type        : <0x57>
    <b5>   DW_AT_location    : 2 字节区块: 91 6c     (DW_OP_fbreg: -20)
 <2><b8>:缩写编号:0
 <1><b9>:缩写编号:8 (DW_TAG_subprogram)
    <ba>   DW_AT_external    : 1
    <ba>   DW_AT_name        : (间接字串,偏移量:0x4b): main
    <be>   DW_AT_decl_file   : 1
    <bf>   DW_AT_decl_line   : 13
    <c0>   DW_AT_type        : <0x57>
    <c4>   DW_AT_low_pc      : 0x400568
    <cc>   DW_AT_high_pc     : 0x15
    <d4>   DW_AT_frame_base  : 1 字节区块: 9c     (DW_OP_call_frame_cfa)
    <d6>   DW_AT_GNU_all_tail_call_sites: 1
 <1><d6>:缩写编号:0

.debug_abbrev 节的内容:

  Number TAG (0x0)
   1      DW_TAG_compile_unit    [has children]
    DW_AT_producer     DW_FORM_strp
    DW_AT_language     DW_FORM_data1
    DW_AT_name         DW_FORM_strp
    DW_AT_comp_dir     DW_FORM_strp
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_stmt_list    DW_FORM_sec_offset
    DW_AT value: 0     DW_FORM value: 0
   2      DW_TAG_base_type    [no children]
    DW_AT_byte_size    DW_FORM_data1
    DW_AT_encoding     DW_FORM_data1
    DW_AT_name         DW_FORM_strp
    DW_AT value: 0     DW_FORM value: 0
   3      DW_TAG_base_type    [no children]
    DW_AT_byte_size    DW_FORM_data1
    DW_AT_encoding     DW_FORM_data1
    DW_AT_name         DW_FORM_string
    DW_AT value: 0     DW_FORM value: 0
   4      DW_TAG_subprogram    [has children]
    DW_AT_external     DW_FORM_flag_present
    DW_AT_name         DW_FORM_strp
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_prototyped   DW_FORM_flag_present
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_frame_base   DW_FORM_exprloc
    DW_AT_GNU_all_tail_call_sites DW_FORM_flag_present
    DW_AT_sibling      DW_FORM_ref4
    DW_AT value: 0     DW_FORM value: 0
   5      DW_TAG_formal_parameter    [no children]
    DW_AT_name         DW_FORM_string
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_location     DW_FORM_exprloc
    DW_AT value: 0     DW_FORM value: 0
   6      DW_TAG_variable    [no children]
    DW_AT_name         DW_FORM_string
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_location     DW_FORM_exprloc
    DW_AT value: 0     DW_FORM value: 0
   7      DW_TAG_variable    [no children]
    DW_AT_name         DW_FORM_strp
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_location     DW_FORM_exprloc
    DW_AT value: 0     DW_FORM value: 0
   8      DW_TAG_subprogram    [no children]
    DW_AT_external     DW_FORM_flag_present
    DW_AT_name         DW_FORM_strp
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_frame_base   DW_FORM_exprloc
    DW_AT_GNU_all_tail_call_sites DW_FORM_flag_present
    DW_AT value: 0     DW_FORM value: 0

.debug_line 节的调试内容转储:

  偏移:                       0x0
  长度:                      69
  DWARF 版本:                2
  导言长度:        35
  最小指令长度:              1
  “is_stmt”的初始值:       1
  行基数:                      -5
  行范围:                      14
  操作码基数:                  13

 操作码:
  操作码 1 具有 0 个参数
  操作码 2 具有 1 个参数
  操作码 3 具有 1 个参数
  操作码 4 具有 1 个参数
  操作码 5 具有 1 个参数
  操作码 6 具有 0 个参数
  操作码 7 具有 0 个参数
  操作码 8 具有 0 个参数
  操作码 9 具有 1 个参数
  操作码 10 具有 0 个参数
  操作码 11 具有 0 个参数
  操作码 12 具有 1 个参数

 目录表为空。

 文件名表 (偏移 0x1c):
  条目    目录    时间    大小    名称
  1    0    0    0    debug_info.c

 行号语句:
  [0x0000002d]  扩充操作码 2: 设置地址为 0x400526
  [0x00000038]  Special opcode 8: advance Address by 0 to 0x400526 and Line by 3 to 4
  [0x00000039]  Special opcode 160: advance Address by 11 to 0x400531 and Line by 1 to 5
  [0x0000003a]  Special opcode 104: advance Address by 7 to 0x400538 and Line by 1 to 6
  [0x0000003b]  Special opcode 132: advance Address by 9 to 0x400541 and Line by 1 to 7
  [0x0000003c]  Special opcode 35: advance Address by 2 to 0x400543 and Line by 2 to 9
  [0x0000003d]  Advance PC by constant 17 to 0x400554
  [0x0000003e]  Special opcode 129: advance Address by 9 to 0x40055d and Line by -2 to 7
  [0x0000003f]  Special opcode 121: advance Address by 8 to 0x400565 and Line by 4 to 11
  [0x00000040]  Special opcode 50: advance Address by 3 to 0x400568 and Line by 3 to 14
  [0x00000041]  Special opcode 62: advance Address by 4 to 0x40056c and Line by 1 to 15
  [0x00000042]  Special opcode 146: advance Address by 10 to 0x400576 and Line by 1 to 16
  [0x00000043]  Special opcode 76: advance Address by 5 to 0x40057b and Line by 1 to 17
  [0x00000044]  Advance PC by 2 to 0x40057d
  [0x00000046]  扩充操作码 1: 序列结束


.debug_str 节的内容:

  0x00000000 73686f72 7420756e 7369676e 65642069 short unsigned i
  0x00000010 6e74006c 6f6e6720 756e7369 676e6564 nt.long unsigned
  0x00000020 20696e74 002f686f 6d652f61 6c69656e  int./home/alien
  0x00000030 74656b2f 74657374 64697231 00756e73 tek/testdir1.uns
  0x00000040 69676e65 64206368 6172006d 61696e00 igned char.main.
  0x00000050 6c6f6e67 20696e74 00646562 75675f69 long int.debug_i
  0x00000060 6e666f2e 6300474e 55204331 3120352e nfo.c.GNU C11 5.
  0x00000070 342e3020 32303136 30363039 202d6d74 4.0 20160609 -mt
  0x00000080 756e653d 67656e65 72696320 2d6d6172 une=generic -mar
  0x00000090 63683d78 38362d36 34202d67 202d6673 ch=x86-64 -g -fs
  0x000000a0 7461636b 2d70726f 74656374 6f722d73 tack-protector-s
  0x000000b0 74726f6e 67006c6f 63616c00 66756e63 trong.local.func
  0x000000c0 0073686f 72742069 6e740073 697a6574 .short int.sizet
  0x000000d0 79706500                            ype.

现在有个问题,我选择账号1进行抖音数据采集视频列表,然后切换平台的小红书的时候,发现账号1在转圈采集,好像显示抖音账号1和小红书账号1显示有点叠,账号1-账号6都检查一下,切换平台的时候,不是要暂停采集数据,是放到后台采集数据,account_widget.py的from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, QFrame, QGridLayout, QMessageBox, QHeaderView, QAbstractItemView, QApplication, QMenu, QFileDialog, QStyle, QProgressBar,QComboBox ) from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt6.QtGui import QFont, QColor from tanchen_v2.core_v1.collector_threads import DouyinDataCollectorThread from tanchen_v2.ui.comment_window import CommentWindow from tanchen_v2.core_v1.loading_indicator import LoadingIndicator from tanchen_v2.core_v1.config_manager import ConfigManager import pandas as pd import json import os from tanchen_v2.core_v1.log_tc import logger class AccountWidget(QWidget): def __init__(self, index, config_manager,platform:str="douyin"): super().__init__() self.index = index self.account_index = index self.data = [] self.current_platform = platform #默认平台 self.platform = platform self.platform_configs=config_manager self.platform_data={}# 按平台存储数据:{"douyin":data,"xiaohongshu":data} self.config_manager = config_manager self.is_editing = True self.collector_thread = None self.comment_window = None self.init_ui() self.load_config() def init_ui(self): main_layout = QVBoxLayout() # 平台选择器 - 移到账号标题上方 # platform_layout = QHBoxLayout() # platform_layout.addWidget(QLabel("选择平台:")) # # self.platform_combo = QComboBox() # self.platform_combo.addItem("抖音", "douyin") # self.platform_combo.addItem("小红书", "xiaohongshu") # self.platform_combo.currentIndexChanged.connect(self.switch_platform) # platform_layout.addWidget(self.platform_combo) # platform_layout.addStretch() # main_layout.addLayout(platform_layout) # 平台选择器放在最上方 # 账号标题 title_layout = self.create_title_layout() main_layout.addLayout(title_layout) # 配置区域 config_layout = self.create_config_layout() main_layout.addLayout(config_layout) # 控制按钮 btn_layout = self.create_button_layout() main_layout.addLayout(btn_layout) # 表格容器 self.table_container = self.create_table_container() main_layout.addWidget(self.table_container, 1) # 状态栏 self.status_label = QLabel("就绪") self.status_label.setStyleSheet(""" QLabel { color: #666; font-size: 12px; padding: 5px; border-top: 1px solid #e0e0e0; } """) main_layout.addWidget(self.status_label) self.setLayout(main_layout) self.set_edit_mode(True) def update_platform(self,platform): """更新账号平台并保留配置""" # 保存当前平台配置 self.platform_configs[self.platform] = self.get_current_config() # 更新平台 self.platform = platform self.platform_config_group.setTitle(f"{platform}配置") # 加载新平台配置 self.update_platform_config_ui() def create_title_layout(self): layout = QHBoxLayout() self.title_label = QLabel(f"账号 {self.index + 1}") self.title_label.setFont(QFont("Arial", 12, QFont.Weight.Bold)) layout.addWidget(self.title_label) layout.addWidget(QLabel("备注:")) self.remark_input = QLineEdit() self.remark_input.setPlaceholderText("账号备注...") self.remark_input.setMaximumWidth(200) layout.addWidget(self.remark_input) self.edit_btn = self.create_icon_button("编辑配置", QStyle.StandardPixmap.SP_FileDialogDetailedView, "#FFC107", "#FFA000", "#FF8F00") self.edit_btn.clicked.connect(self.toggle_edit_mode) layout.addWidget(self.edit_btn) self.save_btn = self.create_icon_button("保存配置", QStyle.StandardPixmap.SP_DialogSaveButton, "#4CAF50", "#388E3C", "#2E7D32") self.save_btn.clicked.connect(self.save_config) layout.addWidget(self.save_btn) layout.addStretch() return layout def create_config_layout(self): layout = QGridLayout() layout.setColumnStretch(1, 1) # 关键字输入 layout.addWidget(QLabel("关键字:"), 0, 0) self.keyword_input = QLineEdit() self.keyword_input.setPlaceholderText("输入搜索关键字...") layout.addWidget(self.keyword_input, 0, 1) # Cookie输入 layout.addWidget(QLabel("Cookie:"), 1, 0) self.cookie_input = QLineEdit() self.cookie_input.setPlaceholderText("输入账号Cookie...") self.cookie_input.setEchoMode(QLineEdit.EchoMode.Password) layout.addWidget(self.cookie_input, 1, 1) # 显示/隐藏Cookie按钮 self.toggle_cookie_btn = QPushButton("显示") self.toggle_cookie_btn.setMaximumWidth(60) self.toggle_cookie_btn.setStyleSheet("QPushButton { padding: 3px; border-radius: 3px; }") self.toggle_cookie_btn.clicked.connect(self.toggle_cookie_visibility) layout.addWidget(self.toggle_cookie_btn, 1, 2) # 评论关键字输入 layout.addWidget(QLabel("评论关键字:"), 2, 0) self.comment_keyword_input = QLineEdit() self.comment_keyword_input.setPlaceholderText("输入评论关键字过滤...支持中文逗号、英文逗号、空格例如:测试1,测试2") layout.addWidget(self.comment_keyword_input, 2, 1) return layout def create_button_layout(self): layout = QHBoxLayout() # 开始采集按钮 self.start_btn = self.create_action_button("开始采集", QStyle.StandardPixmap.SP_MediaPlay, "#4CAF50", "#388E3C", "#2E7D32") self.start_btn.clicked.connect(self.start_collecting) layout.addWidget(self.start_btn) # 停止采集按钮 self.stop_btn = self.create_action_button("停止采集", QStyle.StandardPixmap.SP_MediaStop, "#f44336", "#D32F2F", "#B71C1C") self.stop_btn.clicked.connect(self.stop_collecting) self.stop_btn.setEnabled(False) layout.addWidget(self.stop_btn) # 获取评论按钮 self.get_comments_btn = self.create_action_button("获取评论", QStyle.StandardPixmap.SP_MessageBoxInformation, "#9C27B0", "#7B1FA2", "#4A148C") self.get_comments_btn.clicked.connect(self.get_comments) layout.addWidget(self.get_comments_btn) # 导出数据按钮 self.export_btn = self.create_action_button("导出数据", QStyle.StandardPixmap.SP_DialogSaveButton, "#2196F3", "#1976D2", "#0D47A1") self.export_btn.clicked.connect(self.export_data) layout.addWidget(self.export_btn) # 清空表格按钮 self.clear_table_btn = self.create_action_button("清空表格", QStyle.StandardPixmap.SP_TrashIcon, "#FF9800", "#F57C00", "#EF6C00") self.clear_table_btn.clicked.connect(self.clear_table_data) layout.addWidget(self.clear_table_btn) layout.addStretch() return layout def setup_table_columns(self,platform): """根据平台设置表格列""" if platform == "douyin": self.table.setColumnCount(7) self.table.setHorizontalHeaderLabels( ["序号", "作者昵称", "类型", "视频地址", "评论数量", "发布时间", "视频ID"]) # 设置列宽 self.table.setColumnWidth(0, 60) self.table.setColumnWidth(1, 150) self.table.setColumnWidth(2, 100) self.table.setColumnWidth(3, 300) self.table.setColumnWidth(4, 100) self.table.setColumnWidth(5, 150) self.table.setColumnWidth(6, 200) elif platform == "xiaohongshu": self.table.setColumnCount(8) self.table.setHorizontalHeaderLabels( ["序号", "作者昵称", "类型", "笔记链接", "点赞数", "收藏数", "发布时间", "笔记ID"]) # 设置列宽 self.table.setColumnWidth(0, 60) self.table.setColumnWidth(1, 150) self.table.setColumnWidth(2, 100) self.table.setColumnWidth(3, 300) self.table.setColumnWidth(4, 80) self.table.setColumnWidth(5, 80) self.table.setColumnWidth(6, 150) self.table.setColumnWidth(7, 200) # 添加新平台时只需在这里添加新的列配置 def create_table_container(self): container = QFrame() container.setFrameShape(QFrame.Shape.StyledPanel) container.setStyleSheet("background-color: white; border: 1px solid #E0E0E0; border-radius: 4px;") layout = QVBoxLayout(container) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # v2数据表格 self.table = QTableWidget() self.setup_table_columns(self.current_platform) # 根据平台初始化表格列 self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Interactive) self.table.horizontalHeader().setSectionsMovable(True) self.table.verticalHeader().setVisible(False) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) self.table.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.table.setSortingEnabled(True) self.table.setStyleSheet(""" QTableWidget { background-color: white; alternate-background-color: #f9f9f9; gridline-color: #e0e0e0; } QHeaderView::section { background-color: #f0f0f0; padding: 4px; border: 1px solid #e0e0e0; font-weight: bold; } """) self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu) layout.addWidget(self.table) # 覆盖层用于显示加载指示器 self.overlay = QWidget(container) self.overlay.setGeometry(0, 0, container.width(), container.height()) self.overlay.setStyleSheet("background-color: rgba(255, 255, 255, 0.7);") overlay_layout = QVBoxLayout(self.overlay) overlay_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.loading_indicator = LoadingIndicator() overlay_layout.addWidget(self.loading_indicator) self.overlay.hide() return container def create_icon_button(self, text, icon, base_color, hover_color, pressed_color): button = QPushButton(text) button.setIcon(self.style().standardIcon(icon)) text_color = "black" if base_color == "#FFC107" else "white" button.setStyleSheet(f""" QPushButton {{ background-color: {base_color}; color: {text_color}; padding: 5px; border-radius: 4px; }} QPushButton:hover {{ background-color: {hover_color}; }} QPushButton:pressed {{ background-color: {pressed_color}; }} """) return button def create_action_button(self, text, icon, base_color, hover_color, pressed_color): button = QPushButton(text) button.setIcon(self.style().standardIcon(icon)) button.setStyleSheet(f""" QPushButton {{ background-color: {base_color}; color: white; padding: 8px 16px; border-radius: 5px; font-weight: bold; }} QPushButton:hover {{ background-color: {hover_color}; }} QPushButton:pressed {{ background-color: {pressed_color}; }} QPushButton:disabled {{ background-color: {base_color}80; }} """) return button def toggle_cookie_visibility(self): if self.cookie_input.echoMode() == QLineEdit.EchoMode.Password: self.cookie_input.setEchoMode(QLineEdit.EchoMode.Normal) self.toggle_cookie_btn.setText("隐藏") else: self.cookie_input.setEchoMode(QLineEdit.EchoMode.Password) self.toggle_cookie_btn.setText("显示") def toggle_edit_mode(self): self.set_edit_mode(not self.is_editing) def set_edit_mode(self, edit_mode): self.is_editing = edit_mode self.remark_input.setReadOnly(not edit_mode) self.keyword_input.setReadOnly(not edit_mode) self.cookie_input.setReadOnly(not edit_mode) self.edit_btn.setVisible(not edit_mode) self.save_btn.setVisible(edit_mode) bg_color = "#f5f5f5" if not edit_mode else "white" self.remark_input.setStyleSheet(f"background-color: {bg_color}; border: 1px solid #e0e0e0; border-radius: 3px;") self.keyword_input.setStyleSheet( f"background-color: {bg_color}; border: 1px solid #e0e0e0; border-radius: 3px;") self.cookie_input.setStyleSheet(f"background-color: {bg_color}; border: 1px solid #e0e0e0; border-radius: 3px;") if edit_mode: self.status_label.setText("编辑模式:可以修改配置") else: self.status_label.setText("配置已锁定,点击'编辑配置'修改") def switch_platform(self,new_platform): """切换平台时的处理""" # 保存当前平台的数据 self.save_current_data() # 更新当前平台 self.current_platform = new_platform # 更新表格列 self.setup_table_columns(new_platform) # 加载新平台的配置 self.load_config() # 加载新平台的数据 self.load_platform_data() # 更新状态栏 self.status_label.setText(f"已切换到{new_platform}平台") def save_current_data(self): """保存当前平台的数据到内存""" # 从表格获取当前数据 data = [] for row in range(self.table.rowCount()): row_data = {} for col in range(self.table.columnCount()): item = self.table.item(row, col) if item: header = self.table.horizontalHeaderItem(col).text() row_data[header] = item.text() data.append(row_data) # 保存到平台数据字典 self.platform_data[self.current_platform] = data def load_platform_data(self): """加载当前平台的数据到表格""" data = self.platform_data.get(self.current_platform, []) # 清空表格 self.table.setRowCount(0) if not data: self.status_label.setText(f"{self.current_platform}平台无数据") return # 填充表格 self.table.setRowCount(len(data)) for row, item in enumerate(data): for col in range(self.table.columnCount()): header = self.table.horizontalHeaderItem(col).text() value = item.get(header, "") table_item = QTableWidgetItem(value) table_item.setFlags(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled) self.table.setItem(row, col, table_item) self.status_label.setText(f"已加载{len(data)}条{self.current_platform}数据") def save_config(self): #v2 self.set_edit_mode(False) # 保存配置时按平台存储 config = { "remark": self.remark_input.text(), "keyword": self.keyword_input.text(), "cookie": self.cookie_input.text(), "comment_keyword": self.comment_keyword_input.text(), "platform": self.current_platform # 保存当前平台 } # 使用平台特定的键保存配置 self.config_manager.save_account_config(self.index, config, self.current_platform) self.status_label.setText("配置已保存") self.save_btn.setStyleSheet(""" QPushButton { background-color: #2E7D32; color: white; padding: 5px; border-radius: 4px; } """) QTimer.singleShot(500, self.restore_save_button_style) def restore_save_button_style(self): self.save_btn.setStyleSheet(""" QPushButton { background-color: #4CAF50; color: white; padding: 5px; border-radius: 4px; } QPushButton:hover { background-color: #388E3C; } QPushButton:pressed { background-color: #2E7D32; } """) def load_config(self): # 加载当前平台的配置 config = self.config_manager.load_account_config(self.index, self.current_platform) if config: self.remark_input.setText(config.get("remark", "")) self.keyword_input.setText(config.get("keyword", "")) self.cookie_input.setText(config.get("cookie", "")) self.comment_keyword_input.setText(config.get("comment_keyword", "")) logger.debug(f"平台:{self.current_platform}--账号{self.index + 1}加载配置成功") logger.debug(f"平台:{self.current_platform}--账号{self.index + 1}加载配置:{str(config)}") else: # 如果没有配置,初始化空值 self.remark_input.setText("") self.keyword_input.setText("") self.cookie_input.setText("") self.comment_keyword_input.setText("") logger.debug(f"平台:{ self.current_platform}--账号{self.index + 1}无配置,使用默认值") logger.debug(f"平台:{self.current_platform}--账号{self.index + 1}无配置, 加载配置:{str(config)}") def start_collecting(self): keyword = self.keyword_input.text().strip() cookie = self.cookie_input.text().strip() if not keyword: QMessageBox.warning(self, "输入错误", "请输入搜索关键字") return if not cookie: QMessageBox.warning(self, "输入错误", "请输入账号Cookie") return if self.collector_thread and self.collector_thread.isRunning(): self.collector_thread.stop() self.collector_thread.quit() self.collector_thread.wait(1000) self.overlay.show() self.loading_indicator.start() self.start_btn.setStyleSheet(""" QPushButton { background-color: #2E7D32; color: white; padding: 8px 16px; border-radius: 5px; font-weight: bold; } """) # 使用爬虫工厂创建对应平台的爬虫线程 try: from core.Crawler.crawler_factory import CrawlerFactory self.collector_thread = CrawlerFactory.create_crawler( platform=self.current_platform, account_index=self.index, config={ "keyword": keyword, "cookie": cookie } ) # 设置关键字 self.collector_thread.keyword = keyword except Exception as e: logger.warning(f"创建爬虫失败: {str(e)},{str(self.current_platform)}") self.handle_error(f"创建爬虫失败: {str(e)}") self.overlay.hide() self.loading_indicator.stop() return try: self.collector_thread.data_collected.connect(self.update_data) self.collector_thread.status_updated.connect(self.update_status) self.collector_thread.error_occurred.connect(self.handle_error) self.collector_thread.finished.connect(self.on_collection_finished) self.collector_thread.start(keyword) self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.keyword_input.setEnabled(False) self.cookie_input.setEnabled(False) self.status_label.setText(f"开始采集{self.current_platform}: {keyword}") self.status_label.setStyleSheet("color: #4CAF50; font-weight: bold;") except Exception as e: logger.warning(f"启动爬虫失败: {str(e)}") self.handle_error(f"启动爬虫失败: {str(e)}") def handle_error(self, error_message): self.status_label.setText(error_message) self.status_label.setStyleSheet("color: #f44336; font-weight: bold;") #self.stop_collecting() def update_status(self, message): self.status_label.setText(message) self.status_label.setStyleSheet("color: #4CAF50; font-weight: bold;") def on_collection_finished(self): self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.keyword_input.setEnabled(True) self.cookie_input.setEnabled(True) self.overlay.hide() self.loading_indicator.stop() self.restore_start_button_style() def restore_start_button_style(self): self.start_btn.setStyleSheet(""" QPushButton { background-color: #4CAF50; color: white; padding: 8px 16px; border-radius: 5px; font-weight: bold; } QPushButton:hover { background-color: #388E3C; } QPushButton:pressed { background-color: #2E7D32; } QPushButton:disabled { background-color: #81C784; } """) def stop_collecting(self): if self.collector_thread and self.collector_thread.isRunning(): self.collector_thread.stop() self.collector_thread.quit() self.collector_thread.wait(1000) self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.keyword_input.setEnabled(True) self.cookie_input.setEnabled(True) self.overlay.hide() self.loading_indicator.stop() self.restore_start_button_style() self.status_label.setText("采集已停止") self.status_label.setStyleSheet("color: #f44336; font-weight: bold;") def remove_duplicates(self, data): seen_urls = set() unique_data = [] url_field = "视频地址" if self.current_platform == "douyin" else "笔记链接" for item in data: item_url = item.get(url_field, "") if item_url and item_url not in seen_urls: seen_urls.add(item_url) unique_data.append(item) return unique_data def update_data(self, new_data): logger.debug("new_data",new_data) if not new_data or not isinstance(new_data, list): QMessageBox.warning(self, "数据错误", "接收到无效的数据格式") return if new_data and "状态" in new_data[0] and new_data[0]["状态"] == "错误": error_msg = new_data[0].get("消息", "未知错误") QMessageBox.warning(self, "采集错误", error_msg) self.stop_collecting() return unique_data = self.remove_duplicates(new_data) # 保存到当前平台的数据 self.platform_data[self.current_platform] = unique_data # 更新表格 self.table.setSortingEnabled(False) self.table.setRowCount(len(unique_data)) flags = Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled for row, item in enumerate(unique_data): # 根据平台动态填充表格 if self.current_platform == "douyin": # 抖音数据处理 self.table.setItem(row, 0, QTableWidgetItem(str(item.get("序号", "")))) self.table.setItem(row, 1, QTableWidgetItem(item.get("作者昵称", ""))) self.table.setItem(row, 2, QTableWidgetItem(item.get("类型", ""))) video_url = item.get("视频地址", "") url_item = QTableWidgetItem(video_url) url_item.setForeground(QColor("#0000FF")) url_item.setToolTip(video_url) self.table.setItem(row, 3, url_item) self.table.setItem(row, 4, QTableWidgetItem(str(item.get("评论数量", 0)))) self.table.setItem(row, 5, QTableWidgetItem(item.get("发布时间", ""))) self.table.setItem(row, 6, QTableWidgetItem(item.get("视频ID", ""))) elif self.current_platform == "xiaohongshu": # 小红书数据处理 self.table.setItem(row, 0, QTableWidgetItem(str(item.get("序号", "")))) self.table.setItem(row, 1, QTableWidgetItem(item.get("作者昵称", ""))) self.table.setItem(row, 2, QTableWidgetItem(item.get("类型", ""))) note_url = item.get("笔记链接", "") url_item = QTableWidgetItem(note_url) url_item.setForeground(QColor("#0000FF")) url_item.setToolTip(note_url) self.table.setItem(row, 3, url_item) self.table.setItem(row, 4, QTableWidgetItem(str(item.get("点赞数", 0)))) self.table.setItem(row, 5, QTableWidgetItem(str(item.get("收藏数", 0)))) self.table.setItem(row, 6, QTableWidgetItem(item.get("发布时间", ""))) self.table.setItem(row, 7, QTableWidgetItem(item.get("笔记ID", ""))) # 添加新平台时在这里添加处理逻辑 self.table.setSortingEnabled(True) self.table.scrollToBottom() self.status_label.setText(f"已采集 {len(unique_data)} 条{self.current_platform}数据") self.status_label.setStyleSheet("color: #4CAF50; font-weight: bold;") def export_data(self): # 获取当前平台的数据 current_data = self.platform_data.get(self.current_platform, []) if not current_data: QMessageBox.warning(self, "导出失败", f"没有可导出的{self.current_platform}数据") return try: remark = self.remark_input.text().strip() platform_name = self.current_platform default_name = f"{platform_name}数据_账号{self.index + 1}_{remark}.xlsx" if remark else f"{platform_name}数据_账号{self.index + 1}.xlsx" filename, _ = QFileDialog.getSaveFileName( self, "导出数据", default_name, "Excel文件 (*.xlsx)" ) if not filename: return if not filename.endswith('.xlsx'): filename += '.xlsx' df = pd.DataFrame(current_data) df.to_excel(filename, index=False) self.export_btn.setStyleSheet(""" QPushButton { background-color: #0D47A1; color: white; padding: 8px 16px; border-radius: 5px; font-weight: bold; } """) QTimer.singleShot(500, self.restore_export_button_style) QMessageBox.information(self, "导出成功", f"数据已导出到: {filename}") except Exception as e: QMessageBox.critical(self, "导出错误", f"导出失败: {str(e)}") def restore_export_button_style(self): self.export_btn.setStyleSheet(""" QPushButton { background-color: #2196F3; color: white; padding: 8px 16px; border-radius: 5px; font-weight: bold; } QPushButton:hover { background-color: #1976D2; } QPushButton:pressed { background-color: #0D47A1; } """) def clear_table_data(self): # 获取当前平台的数据 current_data = self.platform_data.get(self.current_platform, []) if not current_data: QMessageBox.information(self, "提示", f"{self.current_platform}平台无数据") return reply = QMessageBox.question( self, "确认清空", f"确定要清空{self.current_platform}的表格数据吗? (共{len(current_data)}条)", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.platform_data[self.current_platform] = [] self.table.setRowCount(0) self.status_label.setText(f"{self.current_platform}数据已清空") def get_comments(self): """修改:处理多个关键字""" # 获取当前平台的数据 current_data = self.platform_data.get(self.current_platform, []) if not current_data: QMessageBox.warning(self, "操作失败", f"没有可用的{self.current_platform}数据") return # 获取关键字并分割 keyword_input = self.comment_keyword_input.text().strip() if keyword_input: # 使用 | 作为分隔符,支持中文逗号、英文逗号、空格 keywords = keyword_input.replace(',', ',').replace(' ', ',') keyword_filter = '|'.join([kw.strip() for kw in keywords.split(',') if kw.strip()]) else: keyword_filter = "" # 构建有效的视频数据 video_data = [] for idx, item in enumerate(current_data): try: # 根据平台获取不同的字段 if self.current_platform == "douyin": video_id = self.extract_douyin_video_id(item.get("视频地址", "")) comment_count = int(item.get("评论数量", 0)) video_url = item.get("视频地址", "") elif self.current_platform == "xiaohongshu": video_id = self.extract_xiaohongshu_note_id(item.get("笔记链接", "")) comment_count = int(item.get("评论数量", 0)) # 假设小红书也有评论数量字段 video_url = item.get("笔记链接", "") else: continue if video_id and comment_count > 0: video_data.append({ "序号": idx + 1, "视频地址": video_url, "视频ID": video_id, "评论数量": comment_count }) except (ValueError, TypeError, KeyError): continue if not video_data: QMessageBox.warning(self, "操作失败", "没有包含评论的视频数据") return try: # 获取当前账号的cookie cookie = self.cookie_input.text().strip() # 如果评论窗口已存在,先关闭它 if hasattr(self, 'comment_window') and self.comment_window: self.comment_window.close() self.comment_window = None self.comment_window = CommentWindow( parent=self, account_index=self.index, video_data=video_data, keyword_filter=keyword_filter, # 传递处理后的关键字 cookie=cookie, # 传递cookie use_proxy=False, # 默认不开启代理 account_keywords=keyword_filter, # 传递账号关键字 platform=self.current_platform # 传递当前平台 ) # 使用独立窗口模式 self.comment_window.setWindowFlag(Qt.WindowType.Window) self.comment_window.finished.connect(self.on_comment_window_closed) self.comment_window.show() except Exception as e: QMessageBox.critical(self, "错误", f"无法打开评论窗口: {str(e)}") def extract_douyin_video_id(self, video_url): """从抖音视频地址中提取视频ID""" # 示例URL: https://www.douyin.com/video/1234567890123456789 parts = video_url.split('/') if len(parts) >= 5: return parts[-1] # 返回最后一部分作为视频ID return "" def extract_xiaohongshu_note_id(self, note_url): """从小红书笔记地址中提取笔记ID""" # 示例URL: https://www.xiaohongshu.com/explore/1234567890abcdef12345678 parts = note_url.split('/') if len(parts) >= 5: return parts[-1] # 返回最后一部分作为笔记ID return "" def extract_video_id(self, video_url): """从视频地址中提取视频ID""" # 示例URL: https://www.douyin.com/video/1234567890123456789 parts = video_url.split('/') if len(parts) >= 5: return parts[-1] # 返回最后一部分作为视频ID return "" def on_comment_window_closed(self): # 安全地关闭评论窗口 if hasattr(self, 'comment_window') and self.comment_window: # 确保停止线程 if hasattr(self.comment_window, 'comment_thread') and self.comment_window.comment_thread.isRunning(): self.comment_window.comment_thread.stop() self.comment_window.comment_thread.quit() self.comment_window.comment_thread.wait(2000) self.comment_window = None def show_context_menu(self, position): menu = QMenu(self) selected_rows = set(item.row() for item in self.table.selectedItems()) if not selected_rows: return copy_row_action = menu.addAction("复制整行") copy_row_action.triggered.connect(lambda: self.copy_selected_row(selected_rows)) copy_cell_action = menu.addAction("复制单元格") copy_cell_action.triggered.connect(self.copy_selected_cell) menu.addSeparator() # 根据平台显示不同的复制选项 if self.current_platform == "douyin": copy_url_action = menu.addAction("复制视频地址") copy_url_action.triggered.connect(lambda: self.copy_selected_column(3)) copy_video_id_action = menu.addAction("复制视频ID") copy_video_id_action.triggered.connect(lambda: self.copy_selected_column(6)) elif self.current_platform == "xiaohongshu": copy_url_action = menu.addAction("复制笔记链接") copy_url_action.triggered.connect(lambda: self.copy_selected_column(3)) copy_note_id_action = menu.addAction("复制笔记ID") copy_note_id_action.triggered.connect(lambda: self.copy_selected_column(7)) copy_author_action = menu.addAction("复制作者昵称") copy_author_action.triggered.connect(lambda: self.copy_selected_column(1)) menu.exec(self.table.viewport().mapToGlobal(position)) def copy_selected_row(self, rows): text = "" for row in sorted(rows): row_data = [] for col in range(self.table.columnCount()): item = self.table.item(row, col) if item: row_data.append(item.text()) text += "\t".join(row_data) + "\n" clipboard = QApplication.clipboard() clipboard.setText(text.strip()) def copy_selected_cell(self): selected_items = self.table.selectedItems() if not selected_items: return text = "\n".join(item.text() for item in selected_items) clipboard = QApplication.clipboard() clipboard.setText(text) def copy_selected_column(self, column): selected_items = [item for item in self.table.selectedItems() if item.column() == column] if not selected_items: return text = "\n".join(item.text() for item in selected_items) clipboard = QApplication.clipboard() clipboard.setText(text) def resizeEvent(self, event): super().resizeEvent(event) if self.table_container: self.overlay.setGeometry(0, 0, self.table_container.width(), self.table_container.height())和main_window.py的import pandas as pd from PyQt6.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QLabel, QTabWidget, QHBoxLayout, QPushButton, QStatusBar, QStyle, QFileDialog, QMessageBox, QDialog, QFormLayout, QLineEdit, QComboBox, QCheckBox ) from PyQt6.QtGui import QFont from PyQt6.QtCore import Qt, QTimer from tanchen_v2.ui.account_widget import AccountWidget from tanchen_v2.core_v1.config_manager import ConfigManager class ProxyConfigDialog(QDialog): def __init__(self, config_manager, parent=None): super().__init__(parent) self.setWindowTitle("天启代理配置") self.setGeometry(200, 200, 500, 300) self.config_manager = config_manager layout = QVBoxLayout() layout.setContentsMargins(15, 15, 15, 15) layout.setSpacing(10) # 加载配置 self.config = self.config_manager.load_proxy_config() or {} form_layout = QFormLayout() form_layout.setSpacing(10) # 创建控件 self.secret_edit = QLineEdit() self.secret_edit.setText(self.config.get("secret", "")) self.type_combo = QComboBox() self.type_combo.addItems(["json", "txt"]) if "type" in self.config: self.type_combo.setCurrentText(self.config["type"]) else: self.type_combo.setCurrentText("json") self.time_combo = QComboBox() self.time_combo.addItems(["3", "5", "10", "15"]) if "time" in self.config: self.time_combo.setCurrentText(self.config["time"]) else: self.time_combo.setCurrentText("5") self.mr_check = QCheckBox("IP去") self.mr_check.setChecked(self.config.get("mr", "1") == "1") self.sign_edit = QLineEdit() self.sign_edit.setText(self.config.get("sign", "")) # 添加表单行 form_layout.addRow("提取秘钥 (secret):", self.secret_edit) form_layout.addRow("返回类型 (type):", self.type_combo) form_layout.addRow("IP使用时长 (time):", self.time_combo) form_layout.addRow("", self.mr_check) # 单独一行显示复选框 form_layout.addRow("用户签名 (sign):", self.sign_edit) layout.addLayout(form_layout) # 按钮 btn_layout = QHBoxLayout() save_btn = QPushButton("保存") save_btn.setStyleSheet(""" QPushButton { background-color: #4CAF50; color: white; padding: 8px 16px; border-radius: 4px; } QPushButton:hover { background-color: #45a049; } """) save_btn.clicked.connect(self.save_config) cancel_btn = QPushButton("取消") cancel_btn.setStyleSheet(""" QPushButton { background-color: #f44336; color: white; padding: 8px 16px; border-radius: 4px; } QPushButton:hover { background-color: #d32f2f; } """) cancel_btn.clicked.connect(self.reject) btn_layout.addStretch() btn_layout.addWidget(save_btn) btn_layout.addWidget(cancel_btn) btn_layout.addStretch() layout.addLayout(btn_layout) self.setLayout(layout) def save_config(self): self.config = { "secret": self.secret_edit.text(), "type": self.type_combo.currentText(), "time": self.time_combo.currentText(), "mr": "1" if self.mr_check.isChecked() else "0", "sign": self.sign_edit.text() } self.config_manager.save_proxy_config(self.config) self.accept() class DouyinDataCollector(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("多平台数据采集工具") self.setGeometry(100, 100, 1400, 800) self.setStyleSheet(""" QMainWindow { background-color: #f5f5f5; } QTabWidget::pane { border: 1px solid #e0e0e0; background: white; } QTabBar::tab { background: #e0e0e0; border: 1px solid #e0e0e0; padding: 8px 16px; border-top-left-radius: 4px; border-top-right-radius: 4px; margin-right: 2px; } QTabBar::tab:selected { background: white; border-bottom: none; } QTabBar::tab:!selected { margin-top: 4px; } """) self.accounts = [] self.config_manager = ConfigManager() self.current_platform = "douyin" self.init_ui() def init_ui(self): main_widget = QWidget() main_layout = QVBoxLayout() main_layout.setContentsMargins(15, 15, 15, 15) main_layout.setSpacing(15) # 标题 title_label = QLabel("多平台数据采集系统") title_label.setFont(QFont("Arial", 18, QFont.Weight.Bold)) title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) title_label.setStyleSheet(""" QLabel { color: #2196F3; padding: 10px; background-color: white; border-radius: 8px; border: 1px solid #e0e0e0; } """) main_layout.addWidget(title_label) ####################### # 平台选择和代理配置按钮 top_control_layout = QHBoxLayout() # 平台选择器 platform_layout = QHBoxLayout() platform_layout.addWidget(QLabel("选择平台:")) self.platform_combo = QComboBox() self.platform_combo.addItem("抖音", "douyin") self.platform_combo.addItem("小红书", "xiaohongshu") self.platform_combo.currentIndexChanged.connect(self.switch_platform) platform_layout.addWidget(self.platform_combo) top_control_layout.addLayout(platform_layout) # 代理配置按钮 top_control_layout.addStretch() self.proxy_config_btn = self.create_button( "天启代理配置", "#FF9800", "#F57C00", "#E65100", QStyle.StandardPixmap.SP_ComputerIcon) self.proxy_config_btn.clicked.connect(self.show_proxy_config) top_control_layout.addWidget(self.proxy_config_btn) main_layout.addLayout(top_control_layout) # 账号标签页 self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.TabPosition.North) self.tabs.setDocumentMode(True) for i in range(6): account_widget = AccountWidget(i, self.config_manager, self.current_platform) self.accounts.append(account_widget) tab_name = self.get_tab_name(i) # 添加平台参数 self.tabs.addTab(account_widget, tab_name) self.tabs.currentChanged.connect(self.update_tab_names) main_layout.addWidget(self.tabs, 1) # 状态栏 self.status_bar = self.statusBar() self.status_bar.setStyleSheet("background-color: #e0e0e0; color: #666; padding: 5px;") self.status_bar.showMessage("就绪 | 数据采集工具 v1.1 | 配置已自动保存") main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) def switch_platform(self): """切换平台时的处理""" new_platform = self.platform_combo.currentData() self.current_platform = new_platform # 更新所有账号的平台 for account in self.accounts: account.switch_platform(new_platform) self.status_bar.showMessage(f"已切换到{self.platform_combo.currentText()}平台") def create_button(self, text, bg_color, hover_color, pressed_color, icon): button = QPushButton(text) button.setIcon(self.style().standardIcon(icon)) button.setStyleSheet(f""" QPushButton {{ background-color: {bg_color}; color: white; padding: 10px 20px; border-radius: 6px; font-weight: bold; font-size: 14px; }} QPushButton:hover {{ background-color: {hover_color}; }} QPushButton:pressed {{ background-color: {pressed_color}; }} """) # 存储基础颜色用于动画 button.setProperty("base_color", bg_color) return button def get_tab_name(self, index): config = self.config_manager.load_account_config(index,self.current_platform) tab_name = f"账号 {index + 1}" if config and config.get("remark"): tab_name = f"{tab_name}: {config['remark']}" return tab_name def update_tab_names(self): for i in range(self.tabs.count()): self.tabs.setTabText(i, self.get_tab_name(i)) def show_proxy_config(self): try: dialog = ProxyConfigDialog(self.config_manager, self) dialog.setWindowModality(Qt.WindowModality.ApplicationModal) if dialog.exec() == QDialog.DialogCode.Accepted: self.status_bar.showMessage("代理配置已保存") self.animate_button(self.proxy_config_btn, "#E65100") except Exception as e: QMessageBox.critical(self, "错误", f"打开代理配置时出错: {str(e)}") def animate_button(self, button, color): original_style = button.styleSheet() button.setStyleSheet(original_style.replace( f"background-color: {button.property('base_color')}", f"background-color: {color}") ) QTimer.singleShot(500, lambda: button.setStyleSheet(original_style)),因为我用的平台是抖音,然后用账号1进行采集数据,视频列表,切换到小红书平台,发现账号1的框里面在转圈圈,用平台是小红书,用账号2进行采集数据视频列表,切换到抖音,发现账号2的框里面在转圈圈,不要停止采集,切换到后台就行
最新发布
07-23
#include "master.h" #include <log_custom.h> #include <cstddef> #include <cstdint> #include <map> #include <string> #include <base/params_manager/params_manager.h> #include <perception_and_ld_map_fusion/data_fusion/geometry_match_info.h> #include "fmt/core.h" #include "lib/sd_navigation/SDMapElementExtract.h" #include "message/env_model/routing_map/routing_map.h" #include "message/env_model/occ/occ.h" #include "message/sensor/camera/bev_lane/bev_lane.h" #include "cyber/event/trace.h" #include "cyber/time/time.h" #include "modules/perception/env/src/lib/base/localmap_conf.h" #include "modules/simulator/deterministic_scheduler/deterministic_scheduler.h" using byd::simulator::deterministic_scheduler::SchedulerUnitManager; using byd::simulator::deterministic_scheduler::TaskType; using namespace cem::fusion; using namespace cem::fusion::extendLane; namespace cem { namespace fusion { std::shared_ptr<cem::fusion::Master> MasterPtr = nullptr; std::shared_ptr<MasterInterface> GetMasterInstance() { if (!MasterPtr) { MasterPtr = std::shared_ptr<Master>(new Master("modules/perception/env", "local_map")); } return MasterPtr; } Master::Master(const std::string &confdir, const std::string &name) { conf_abs_path_ = confdir + std::string("/"); VLOG(1) << "conf_abs_path_ = " << conf_abs_path_; Init(); } void Master::Init() { LoadConfig(); InitMember(); } void Master::LoadConfig() { ParamsManager::Instance()->ReadJsonParams(conf_abs_path_ + "conf/TrafficRoadFusion/E2_Conf_TrafficRoadFusion.json", conf_abs_path_ + "conf/TrafficRoadFusion/E2_Conf_MeasurementEvaluation.json"); } void Master::InitMember() { routing_map_.reset(new RoutingMap()); pre_processor_.reset(new PreProcessor()); post_processor_.reset(new PostProcessor()); messenger_.reset(new Messenger()); stopline_mapping_.reset(new StoplineMapping); traffic_light_mapping_.reset(new TrafficLightMapping); bev_map_processor_.reset(new BevMapPreProcessor()); fusion_manager_.reset(new FusionManager()); fusion_manager_->Init(); map_fusion_.reset(new MapFusion); lane_guidance_.reset(new LaneGuidance); lane_guidance_city_.reset(new LaneGuidanceCity); env_info_processor_.reset(new navigation::EnvInfoProcessor()); traffic_flow_mapping_.reset(new TrafficFlowMapping); toggle_to_bev_info_.push(MapType::BEV_MAP); cross_road_construction_.reset(new CrossRoadConstruction); cross_road_construction_->Init(); extend_lane_.reset(new ExtendLane); debug_infos_.reset(new DebugInfos); sd_navigation_base_ = std::make_shared<navigation::SdNavigationBase>(); sd_navigation_highway_ = std::make_shared<navigation::SdNavigationHighway>(); sd_navigation_city_ = std::make_shared<navigation::SdNavigationCity>(); if (FLAGS_init_map_source == "ld" || FLAGS_init_map_source == "LD") { toggle_to_bev_info_.push(MapType::HD_MAP); } else { toggle_to_bev_info_.push(MapType::BEV_MAP); } } #define MapLessFlag void Master::Proc() { env_status_ = std::make_shared<byd::msg::orin::routing_map::EnvStatus>(); //get previous cross_road status and cross road distance pre_processor_->is_cross_road_status = cross_road_construction_->IsCrossRoad(); pre_processor_->cross_road_distance = cross_road_construction_->CrossRoadDis(); pre_processor_->Process(); //get previous cross_road status pre_processor_->is_cross_road_status = cross_road_construction_->IsCrossRoad(); RoutingMapPtr routing_map_raw{nullptr}; SensorDataManager::Instance()->GetLatestSensorFrame(routing_map_raw); auto raw_bev_map_ptr = INTERNAL_PARAMS.raw_bev_data.GetRawBevMapPtr(); ///高速和城区的切换 bool IS_ON_HIGHWAY_FLAG = false; { IS_ON_HIGHWAY_FLAG = GetIsOnHighwayFlag(routing_map_raw); if (IS_ON_HIGHWAY_FLAG) { sd_navigation_base_ = sd_navigation_highway_; } else { sd_navigation_base_ = sd_navigation_city_; } } env_status_->set_is_on_freeway(IS_ON_HIGHWAY_FLAG == true); DetectBevMap bev_map = pre_processor_->GetGlobalBevMapPtr(); ///停止线处理 stopline_mapping_->Process(); const auto &bev_stopline_objs = stopline_mapping_->GetStopLines(); ///导航推荐 std::vector<std::pair<uint64_t, std::pair<int, int>>> guide_lane_result = {}; std::vector<uint64_t> sd_guide_lane_ids_ref = {}; sd_navigation_base_->Proc(routing_map_raw, raw_bev_map_ptr, bev_map.bev_map_ptr, guide_lane_result, sd_guide_lane_ids_ref); auto junctions_ptr = INTERNAL_PARAMS.navigation_info_data.GetJunctionInfoCityPtr(); if ((!IS_ON_HIGHWAY_FLAG) && (routing_map_raw != nullptr)) { auto trajectory = sd_navigation_city_->GetNextRightPoints(routing_map_raw); pre_processor_->SetRightTurnTrajectory(routing_map_raw->header.timestamp, trajectory); } ///env_info env_info_processor_->SetIsOnHighway(IS_ON_HIGHWAY_FLAG); env_info_processor_->Process(bev_stopline_objs); const EnvInfo *env_info = env_info_processor_->GetEnvInfo(); if (bev_map.bev_map_ptr != nullptr) { bev_map.bev_map_ptr->env_info = *env_info; } bev_map_processor_->Process(bev_map, sd_guide_lane_ids_ref); fusion_manager_->Process(); const auto routing_map = pre_processor_->routing_map_pre_processor_.GetRoutingMap(); if (routing_map != nullptr) { routing_map->env_info = *env_info; } extend_lane_->Process(bev_map); bev_ld_map_geometry_match_info_.CalculateMapGeometryMatchInfo(fusion_manager_->GetBevMapInfo(), fusion_manager_->GetLdMapInfo()); // cross road part cross_road_construction_->SetGeometryMatchInfo(&bev_ld_map_geometry_match_info_); cross_road_construction_->Process(bev_map.bev_map_ptr, bev_map.Twb, raw_bev_map_ptr, junctions_ptr); // Post-processing of the bev map and intergration the guide lane, virtual // lane, traffic light status, etc. lane_guidance_city_->Process(routing_map_raw, bev_map, bev_stopline_objs, guide_lane_result); traffic_light_mapping_->Process(routing_map_raw, routing_map, lane_guidance_city_->GetMapInfo(), sd_navigation_city_); GeometryMatchResult match_detail; auto map_convert_type = MapToggleMode(match_detail); messenger_->PulishDiagno(); messenger_->PulishTrafficLightE2E(traffic_light_mapping_->GetTrafficLightsE2E()); bool used_ld_map = toggle_to_bev_info_.back() == MapType::HD_MAP; uint32_t map_counter = 0; // std::string map_type_str = used_ld_map ? "ld" : "bev"; map_counter = used_ld_map ? (routing_map ? routing_map->header.cycle_counter : 2) : (lane_guidance_city_->GetMapInfo() ? lane_guidance_city_->GetMapInfo()->header.cycle_counter : 1); const auto &map_convert_params = ParamsManager::Instance()->GetMapConvertParams(); if (IS_ON_HIGHWAY_FLAG) { used_ld_map = false; env_status_->clear_hd_map_suppression_reason(); env_status_->clear_perception_map_suppression_reason(); if (env_info->is_switched_to_LD_ && match_detail.fail_reason != kLostHdMapLane) { env_status_->set_perception_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IN_THE_RAMP); used_ld_map = true; } else { if (match_detail.fail_reason == kLostHdMapLane) { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_OUTSIDE_MAP); } else { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_NOT_IN_THE_RAMP); } used_ld_map = false; } env_status_->clear_distance_to_next_junction(); env_status_->clear_distance_to_passed_junction(); env_status_->clear_left_laneline_match_info(); env_status_->clear_right_laneline_match_info(); // todo: add env status here } else { if (map_convert_params.map_stay_ld) { used_ld_map = true; } else if (map_convert_params.map_stay_bev) { used_ld_map = false; } } std::string map_type_str = used_ld_map ? "ld" : "bev"; if (!map_convert_params.map_stay_ld && used_ld_map) { const bool ld_has{false}; // const bool ld_has = bev_ld_map_geometry_match_info_.LDHasEnoughPoints(); // const bool bev_has = bev_ld_map_geometry_match_info_.BEVHasEnoughPoints(); if (!routing_map || routing_map->route.navi_start.section_id == 0U) { used_ld_map = false; if (!toggle_to_bev_info_.empty()) { toggle_to_bev_info_.pop(); } toggle_to_bev_info_.emplace(MapType::BEV_MAP); if (!routing_map) { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_NO_HD_MAP_FRAME); } else { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_NO_NAVIGATION_START_SECTION); } env_status_->clear_perception_map_suppression_reason(); map_type_str = fmt::format("bev ld_less_points.[{:d}-{}-{:d}]", static_cast<bool>(routing_map), routing_map ? routing_map->route.navi_start.section_id : 0, ld_has); } } RoutingMapPtr routing_map_copy_ptr = nullptr; if (nullptr != routing_map) { bool use_ld_tmp = used_ld_map; if (true == used_ld_map) { routing_map->sensor_status_info.sensor_status = 2; } post_processor_->run(routing_map, used_ld_map); if (use_ld_tmp && !used_ld_map) { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_CLOSE_TO_HIGH_LEVEL_ROAD); env_status_->clear_perception_map_suppression_reason(); env_status_->clear_distance_to_next_junction(); env_status_->clear_distance_to_passed_junction(); env_status_->clear_left_laneline_match_info(); env_status_->clear_right_laneline_match_info(); } map_fusion_->SetGeometryMatchInfo(&bev_ld_map_geometry_match_info_); routing_map_copy_ptr = std::make_shared<RoutingMap>(*routing_map); map_fusion_->Process(bev_map, routing_map_copy_ptr); } if (used_ld_map) { env_status_->set_map_source(byd::msg::orin::routing_map::EnvStatus::EnvSource::EnvStatus_EnvSource_HD_MAP); } else { env_status_->set_map_source(byd::msg::orin::routing_map::EnvStatus::EnvSource::EnvStatus_EnvSource_PERCEPTION_MAP); } #ifdef MapLessFlag used_ld_map = false; #endif debug_infos_->SetDebugInfo(used_ld_map, lane_guidance_city_->GetMapInfo(), lane_guidance_city_->GetTwb(), routing_map_copy_ptr); messenger_->PulishFusionMap(!used_ld_map, lane_guidance_city_->GetMapInfo(), routing_map_copy_ptr, bev_stopline_objs, IS_ON_HIGHWAY_FLAG, sd_navigation_city_->GetInfo()); if (auto map_output = messenger_->GetFusionMapInfo(); map_output) { double adc_to_junction = traffic_light_mapping_->DistanceToJunction(); uint64_t ld_counter = routing_map == nullptr ? 0 : routing_map->header.cycle_counter; uint64_t bev_counter = lane_guidance_city_->GetMapInfo() == nullptr ? 0 : lane_guidance_city_->GetMapInfo()->header.cycle_counter; map_output->mutable_map_info()->mutable_env_status()->CopyFrom(*env_status_); map_output->mutable_map_info()->set_map_version( fmt::format("map_type:{} ld_counter:{} bev_counter:{} vision_counter:{} " "adc_to_junction:{:.2f} map_change_type:{} " "stay_ld:{:d} stay_bev:{:d} IS_ON_HIGHWAY_FLAG:{:d} " "used_ld_map:{:d} is_switched:{:d}", map_type_str, ld_counter, bev_counter, traffic_light_mapping_->GetPerceptionCyberCounter(), adc_to_junction, to_string(map_convert_type), map_convert_params.map_stay_ld, map_convert_params.map_stay_bev, IS_ON_HIGHWAY_FLAG, used_ld_map, env_info->is_switched_to_LD_) + sd_navigation_city_->GetInfo()); map_output->mutable_map_info()->set_engine_version( bev_ld_map_geometry_match_info_.GetDebugInfo() + fmt::format(" is_keep_lane:{:d} is_right:{:d} ", is_lane_keeping_, sd_navigation_city_->IsEgoInDedicatedRightLane()) + traffic_light_mapping_->TrafficLightLaneDebug()); GEOMETRY_LOG << map_output->map_info().map_version(); TRAFFIC_LOG << map_output->map_info().engine_version(); } } bool Master::GetIsOnHighwayFlag(const RoutingMapPtr &routing_map_raw) { auto is_on_highway_flag = [](const SDSectionInfo &sd_section_info) { auto &current_road_class = sd_section_info.road_class; uint32_t current_section_type = sd_section_info.link_type; uint64_t sd_section_id = sd_section_info.id / 10; if ((sd_section_id == 1678534592) || (sd_section_id == 1699361336) || (sd_section_id == 1699374097) || (sd_section_id == 1691246158) || (sd_section_id == 1607693456) || (sd_section_id == 1607693601) || (sd_section_id == 1648672436) || (sd_section_id == 1648672824) || (sd_section_id == 1678890528) || (sd_section_id == 1705771743) || (sd_section_id == 1705771555) || (sd_section_id == 1699402002) || (sd_section_id == 1699402004) || (sd_section_id == 1613352349) || (sd_section_id == 1678533134) || (sd_section_id == 1699372997) || (sd_section_id == 1699374094) || (sd_section_id == 1607690036) || (sd_section_id == 1607690332) || (sd_section_id == 1664789284) || (sd_section_id == 1664789271) || (sd_section_id == 1648672814) || (sd_section_id == 1678890452) || (sd_section_id == 1699443498) || (sd_section_id == 1705771744) || (sd_section_id == 1705771880) || (sd_section_id == 1705771278) || (sd_section_id == 1705771745) || (sd_section_id == 1699402006) || (sd_section_id == 1699402007) || (sd_section_id == 1705771745) || (sd_section_id == 1663998523) || (sd_section_id == 1582251492)) { return false; } if (current_road_class == SDRoadClass::SD_HIGHWAY || current_road_class == SDRoadClass::SD_CITY_FAST_WAY || (current_section_type & (uint32_t)SDLinkTypeMask::SDLT_IC) == (uint32_t)SDLinkTypeMask::SDLT_IC || (current_section_type & (uint32_t)SDLinkTypeMask::SDLT_JCT) == (uint32_t)SDLinkTypeMask::SDLT_JCT || (current_section_type & (uint32_t)SDLinkTypeMask::SDLT_SERVICE) == (uint32_t)SDLinkTypeMask::SDLT_SERVICE) { return true; } return false; }; auto is_highway_ramp = [](const SDSectionInfo &sd_section_info) { uint32_t section_type = sd_section_info.link_type; if ((section_type & (uint32_t)SDLinkTypeMask::SDLT_JCT) == (uint32_t)SDLinkTypeMask::SDLT_JCT || (section_type & (uint32_t)SDLinkTypeMask::SDLT_IC) == (uint32_t)SDLinkTypeMask::SDLT_IC || (section_type & (uint32_t)SDLinkTypeMask::SDLT_SERVICE) == (uint32_t)SDLinkTypeMask::SDLT_SERVICE || (section_type & (uint32_t)SDLinkTypeMask::SDLT_PARK) == (uint32_t)SDLinkTypeMask::SDLT_PARK || (section_type & (uint32_t)SDLinkTypeMask::SDLT_RAMP) == (uint32_t)SDLinkTypeMask::SDLT_RAMP || (sd_section_info.road_class == SDRoadClass::SD_OTHER_ROAD && (section_type & (uint32_t)SDLinkTypeMask::SDLT_POICONNECTION) == (uint32_t)SDLinkTypeMask::SDLT_POICONNECTION)) { return true; } else { return false; } }; auto is_highway_city_connection = [is_on_highway_flag, is_highway_ramp]( const SDSectionInfo &pre_sd_section_info, const SDSectionInfo &cur_sd_section_info, const SDSectionInfo &suc_sd_section_info) { uint32_t cur_section_type = cur_sd_section_info.link_type; if ((pre_sd_section_info.successor_section_id_list.size() == 1) && (is_on_highway_flag(pre_sd_section_info)) && ((cur_section_type & (uint32_t)SDLinkTypeMask::SDLT_IC) == (uint32_t)SDLinkTypeMask::SDLT_IC) && (!is_on_highway_flag(suc_sd_section_info) && (!is_highway_ramp(suc_sd_section_info)))) { return true; } else { return false; } }; if (routing_map_raw == nullptr) { return false; } auto &sd_mpp_sections = routing_map_raw->sd_route.mpp_sections; uint64_t sd_ego_sectionid = routing_map_raw->sd_route.navi_start.section_id; int ego_section_index = -1; for (unsigned int i = 0; i < sd_mpp_sections.size(); i++) { if (sd_ego_sectionid == sd_mpp_sections[i].id) { ego_section_index = i; break; } } if (ego_section_index == -1) { return routing_map_raw->is_on_highway; } auto &current_section = sd_mpp_sections[ego_section_index]; bool IS_ON_HIGHWAY_FLAG = routing_map_raw->is_on_highway; if (IS_ON_HIGHWAY_FLAG) { if (!is_on_highway_flag(current_section)) { return false; } if (ego_section_index >= 1 && ego_section_index < sd_mpp_sections.size() - 1) { if (is_highway_city_connection(sd_mpp_sections[ego_section_index - 1], current_section, sd_mpp_sections[ego_section_index + 1])) { return false; } } if (is_highway_ramp(current_section)) { return true; } double length_tmp = fabs(current_section.length - routing_map_raw->sd_route.navi_start.s_offset); double max_range = 1500; for (unsigned int i = ego_section_index + 1; i < sd_mpp_sections.size(); i++) { if (max_range < length_tmp) { break; } if (i >= 1 && i < sd_mpp_sections.size() - 1) { if (is_highway_city_connection(sd_mpp_sections[i - 1], sd_mpp_sections[i], sd_mpp_sections[i + 1])) { IS_ON_HIGHWAY_FLAG = false; break; } } if (is_on_highway_flag(sd_mpp_sections[i]) && is_highway_ramp(sd_mpp_sections[i])) { break; } if (!is_on_highway_flag(sd_mpp_sections[i])) { IS_ON_HIGHWAY_FLAG = false; break; } length_tmp += sd_mpp_sections[i].length; } return IS_ON_HIGHWAY_FLAG; } else { return false; } } MapConvertType Master::MapToggleMode(GeometryMatchResult &match_detail) { static constexpr size_t queue_max_size = 30; if (bev_ld_matched_info_.size() > queue_max_size) { bev_ld_matched_info_.pop(); } if (toggle_to_bev_info_.size() > queue_max_size) { toggle_to_bev_info_.pop(); } constexpr double distance_bev_to_ld_max = 150; constexpr double distance_bev_to_ld_min = 0.5; constexpr double distance_ld_to_bev_max = 150; constexpr double distance_threshold = 100; const bool bev_ld_boundary_matched = bev_ld_map_geometry_match_info_.IsLaneBoundaryMatch(match_detail); double adc_to_junction = traffic_light_mapping_->DistanceToJunction(); bool is_drive_into_unreliable_road = traffic_light_mapping_->IsUnreliableRoadCrossJunction(); is_drive_into_unreliable_road = false; bool distance_bev_to_ld = adc_to_junction < distance_bev_to_ld_max && adc_to_junction > distance_bev_to_ld_min; bool bev_ld_matched_current_period = distance_bev_to_ld && bev_ld_boundary_matched; double dis_ego_to_passed_junction = traffic_light_mapping_->GetPreviousJunctionEgoPassed(); LaneInfo ego_lane_ld = bev_ld_map_geometry_match_info_.GetLdBevLane(); bool ego_in_junction = ego_lane_ld.id != 0 && ego_lane_ld.type == cem::message::env_model::LaneType::LANE_VIRTUAL_JUNCTION; PLanningResultPtr planning_result{nullptr}; SensorDataManager::Instance()->GetLatestSensorFrame(planning_result); if (planning_result) { if (planning_result->lc_state == PLanningResultData::Keeping) { is_lane_keeping_ = true; } else { is_lane_keeping_ = false; } } if (!env_status_->is_on_freeway() && ((match_detail.fail_reason == MatchFailReason::kNotFailed) || (match_detail.fail_reason == MatchFailReason::kMatchFailed))) { env_status_->set_distance_to_next_junction(adc_to_junction); env_status_->set_distance_to_passed_junction(dis_ego_to_passed_junction); env_status_->mutable_left_laneline_match_info()->set_average_offset(match_detail.left_offset_average); env_status_->mutable_left_laneline_match_info()->set_max_offset(match_detail.left_offset_max); env_status_->mutable_right_laneline_match_info()->set_average_offset(match_detail.right_offset_average); env_status_->mutable_right_laneline_match_info()->set_max_offset(match_detail.right_offset_max); } if (env_status_->is_on_freeway() && match_detail.fail_reason == MatchFailReason::kLostHdMapLane && (!is_lane_keeping_)) { match_detail.fail_reason = MatchFailReason::kNotFailed; } GEOMETRY_LOG << fmt::format( " bev_ld_boundary_matched:{:d} distance_to_junction:{:.2f} ego_in_junction:{:d} vector_type:{:d} size:{:d} " "ego_dis_to_last_junction:{:.2f} is_lane_keeping:{:d} bev_ld_matched_current_period:{:d}", bev_ld_boundary_matched, adc_to_junction, ego_in_junction, (toggle_to_bev_info_.empty() ? MapType::UNKOWN_MAP : toggle_to_bev_info_.back()), toggle_to_bev_info_.size(), dis_ego_to_passed_junction, is_lane_keeping_, bev_ld_matched_current_period); switch (toggle_to_bev_info_.back()) { case MapType::HD_MAP: { if (adc_to_junction > distance_ld_to_bev_max && bev_ld_boundary_matched && dis_ego_to_passed_junction > distance_threshold && is_lane_keeping_) { toggle_to_bev_info_.emplace(MapType::BEV_MAP); env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_PASSED_JUNCTION); return MapConvertType::LD_2_BEV; } if (!is_lane_keeping_) { env_status_->set_perception_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IS_CHANGING_LANE); } else if (adc_to_junction <= distance_ld_to_bev_max || dis_ego_to_passed_junction <= distance_threshold) { env_status_->set_perception_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IN_JUNCTION); } else { env_status_->set_perception_map_suppression_reason(byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason:: EnvStatus_MapSuppressionReason_UNABLE_TO_MATCH_PERCEPTION_WITH_HD_MAP); } toggle_to_bev_info_.emplace(MapType::HD_MAP); return MapConvertType::LD_2_LD; } case MapType::BEV_MAP: { if (bev_ld_matched_current_period && is_lane_keeping_ && (!is_drive_into_unreliable_road)) { toggle_to_bev_info_.emplace(MapType::HD_MAP); env_status_->set_perception_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IN_JUNCTION); return MapConvertType::BEV_2_LD; } if (adc_to_junction < distance_bev_to_ld_min) { toggle_to_bev_info_.emplace(MapType::UNKOWN_MAP); env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_UNKNOWN_REASON); return MapConvertType::BEV_2_UNKNOWN; } toggle_to_bev_info_.emplace(MapType::BEV_MAP); if (!is_lane_keeping_) { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IS_CHANGING_LANE); } else if (is_drive_into_unreliable_road) { env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_CLOSE_TO_HIGH_LEVEL_ROAD); } else { if (adc_to_junction > distance_bev_to_ld_max) { env_status_->set_hd_map_suppression_reason(byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason:: EnvStatus_MapSuppressionReason_EGO_NOT_CLOSE_ENOUGH_TO_JUNCTION); } else { env_status_->set_hd_map_suppression_reason(byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason:: EnvStatus_MapSuppressionReason_UNABLE_TO_MATCH_PERCEPTION_WITH_HD_MAP); } } return MapConvertType::BEV_2_BEV; } case MapType::UNKOWN_MAP: { if (bev_ld_matched_current_period && is_lane_keeping_) { toggle_to_bev_info_.emplace(MapType::HD_MAP); env_status_->set_perception_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_EGO_IN_JUNCTION); return MapConvertType::UNKNOWN_2_LD; } if (adc_to_junction > distance_bev_to_ld_min * 2 && is_lane_keeping_) { toggle_to_bev_info_.emplace(MapType::BEV_MAP); env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_UNKNOWN_REASON); return MapConvertType::UNKNOWN_2_BEV; } env_status_->set_hd_map_suppression_reason( byd::msg::orin::routing_map::EnvStatus::MapSuppressionReason::EnvStatus_MapSuppressionReason_UNKNOWN_REASON); toggle_to_bev_info_.emplace(MapType::UNKOWN_MAP); return MapConvertType::UNKNOWN_2_UNKNOWN; } default: { toggle_to_bev_info_.emplace(MapType::BEV_MAP); return MapConvertType::DEFAULT_BEV; } } toggle_to_bev_info_.emplace(MapType::BEV_MAP); return MapConvertType::DEFAULT_BEV; } /**********************************发送消息的数据结构接口************************************/ std::shared_ptr<byd::msg::basic::ModuleStatus> Master::GetModuleStatusPtr() { return this->messenger_->GetDiagnoInfo(); } std::shared_ptr<byd::msg::orin::routing_map::RoutingMap> Master::GetENVRoutingMapPtr() { return this->messenger_->GetRoutingMapInfo(); } std::shared_ptr<byd::msg::orin::routing_map::TrafficLightsE2EInfo> Master::GetTrafficLightsE2EPtr() { return this->messenger_->GetTrafficLightsE2EInfo(); } /***************************接收消息callback函数 * 在此处实现************************************/ void Master::OnVehInfoCallback(const std::shared_ptr<byd::msg::drivers::VehInfo> &msg) { this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::VehicleSignal>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Bcm50msPdu02>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Ibs20msPdu08>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Imcu20msPdu05>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Eps010msPdu00>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Ibs20msPdu15>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Sas10msPdu00>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Mpd100msPdu04>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::Icm100msPdu29>(msg); this->messenger_->Callback<byd::msg::drivers::VehInfo, cem::message::sensor::AdasIpd100msPdu12>(msg); } void Master::OnEnvModelLocalMapInfoCallback(const std::shared_ptr<byd::msg::env_model::LocalMapInfo> &msg) { this->messenger_->Callback<byd::msg::env_model::LocalMapInfo, cem::message::sensor::BevMapInfo>(msg); } void Master::OnRoutingMapCallBack(const std::shared_ptr<byd::msg::orin::routing_map::RoutingMap> &msg) { this->messenger_->Callback<byd::msg::orin::routing_map::RoutingMap, cem::message::env_model::RoutingMap>(msg); } void Master::OnMsgMapEventCallback(const std::shared_ptr<byd::msg::orin::routing_map::MapEvent> &msg) { this->messenger_->Callback<byd::msg::orin::routing_map::MapEvent, cem::message::env_model::MapEvent>(msg); } /* OnMsgMapEventCallback */ void Master::OnPercepTrfInfoCallback(const std::shared_ptr<byd::msg::perception::PerceptionTrafficInfo> &msg) { this->messenger_->Callback<byd::msg::perception::PerceptionTrafficInfo, cem::message::sensor::PercepTrfInfo>(msg); } void Master::OnPerceptionObstaclesInfoCallback(const std::shared_ptr<byd::msg::perception::PerceptionObstacles> &msg) { this->messenger_->Callback<byd::msg::perception::PerceptionObstacles, cem::message::sensor::FusObjInfo>(msg); } void Master::OnMsgDriverInsCallback(const std::shared_ptr<byd::msg::drivers::Ins> &msg) { this->messenger_->Callback<byd::msg::drivers::Ins, cem::message::sensor::LocalizationData>(msg); } void Master::OnMsgLocationDrCallback(const std::shared_ptr<byd::msg::localization::LocalizationEstimate> &msg) { this->messenger_->Callback<byd::msg::localization::LocalizationEstimate, cem::message::sensor::LocalizationData>(msg); } // void Master::OncanoutCallback( // const std::shared_ptr<byd::msg::orin::sm_msgs::MsgSM_BYD_CAN_Output> &msg) // { // this->messenger_->Callback<byd::msg::orin::sm_msgs::MsgSM_BYD_CAN_Output, // cem::message::sensor::CAN1>(msg); // } void Master::OnEnvModelLidaRoadedgeCallback(const std::shared_ptr<byd::msg::env_model::LocalMapInfo> &msg) { this->messenger_->Callback<byd::msg::env_model::LocalMapInfo, cem::message::sensor::LidarRoadEdgeInfo>(msg); } void Master::OnPlanResultCallback(const std::shared_ptr<byd::msg::planning::PLanningResultProto> &msg) { this->messenger_->Callback<byd::msg::planning::PLanningResultProto, cem::message::env_model::PLanningResultData>(msg); } void Master::OnPlanFuncStateCallback(const std::shared_ptr<byd::msg::pnc::PlanFuncState> &msg) { this->messenger_->Callback<byd::msg::pnc::PlanFuncState, cem::message::env_model::PLanFuncState>(msg); } void Master::OncanoutCallback(const std::shared_ptr<byd::msg::orin::sm_msgs::MsgSM_BYD_CAN_Output> &msg) { this->messenger_->Callback<byd::msg::orin::sm_msgs::MsgSM_BYD_CAN_Output, cem::message::env_model::CAN1>(msg); } void Master::OnOccCallback(const std::shared_ptr<byd::msg::perception::OCCInfo>& msg){ this->messenger_->Callback<byd::msg::perception::OCCInfo,cem::env_model::occ::OCCInfo>(msg); return; } /***************************************************************************************************/ } // namespace fusion } // namespace cem 解读下代码
07-19
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值