五、try_update_binary详细流程

五、try_update_binary详细流程

本篇将介绍try_update_binary 执行升级包中的updater_binary可执行文件解析update-scrypt进行升级流程

 

bootable/recovery/install.cpp
static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
                                  std::vector<std::string>* log_buffer, int retry_count,
                                  int* max_temperature) {
  ......
  ......
  //根据之前install.cpp的流程分析,这里传入升级包的路径,和从内存地址中读出的升级包数据
  int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
  ......
  ......
}

try_update_binary方法

bootable/recovery/install.cpp
// If the package contains an update binary, extract it and run it.
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
                             std::vector<std::string>* log_buffer, int retry_count,
                             int* max_temperature) {
  //将META/com/android/metadata中一些值写入到last_install中
  read_source_target_build(zip, log_buffer);
  //调用pipe在父子进程间使用管道
  //pipefd[0]用于读取数据 ,pipefd[1]用于写入数据
  int pipefd[2];
  pipe(pipefd);

  std::vector<std::string> args;
#ifdef AB_OTA_UPDATER
  int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count,
                                  pipefd[1], &args);
#else
  //找到update-binary,放入到tmp路径,和其他一些参数放入args容器中
  int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1],
                                  &args);
#endif
  if (ret) {
    close(pipefd[0]);
    close(pipefd[1]);
    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
    return ret;
  }
  // 将args中的参数转换为数组形式
  const char* chr_args[args.size() + 1];
  chr_args[args.size()] = nullptr;
  for (size_t i = 0; i < args.size(); i++) {
    chr_args[i] = args[i].c_str();
  }
  //fork一个子进程
  pid_t pid = fork();

  if (pid == -1) {
    close(pipefd[0]);
    close(pipefd[1]);
    PLOG(ERROR) << "Failed to fork update binary";
    log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
    return INSTALL_ERROR;
  }

  if (pid == 0) {
    umask(022);
    close(pipefd[0]);
    //exevc调用可执行程序 执行updater-binary
    //int execl(const char *path, const char *arg, ...);        
    //path:可执行文件的路径
    //arg:传递的参数(一定要传递NULL)
    execv(chr_args[0], const_cast<char**>(chr_args));
    // Bug: 34769056
    // We shouldn't use LOG/PLOG in the forked process, since they may cause
    // the child process to hang. This deadlock results from an improperly
    // copied mutex in the ui functions.
    fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
    _exit(EXIT_FAILURE);
  }
  close(pipefd[1]);

  std::atomic<bool> logger_finished(false);
  std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));

  *wipe_cache = false;
  bool retry_update = false;

  char buffer[1024];
  //函数说明:fdopen()会将参数fildes 的文件描述词, 转换为对应的文件指针后返回.
  FILE* from_child = fdopen(pipefd[0], "r");
  //C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。
  //当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
  //下面的while循环主要为了更新父进程UI,并通过管道与父进程交互
  while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
    std::string line(buffer);
    size_t space = line.find_first_of(" \n");
    std::string command(line.substr(0, space));
    if (command.empty()) continue;
    ......
    ......
  return INSTALL_SUCCESS;
}

update_binary_command方法

int update_binary_command(const std::string& package, ZipArchiveHandle zip,
                          const std::string& binary_path, int retry_count, int status_fd,
                          std::vector<std::string>* cmd) {
  CHECK(cmd != nullptr);

  // On traditional updates we extract the update binary from the package.
  //constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。
  //最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,
  //而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。
  //constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。
  static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
  //在升级包中找到脚本解释器updater-binary
  ZipString binary_name(UPDATE_BINARY_NAME);
  ZipEntry binary_entry;
  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
    LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
    return INSTALL_CORRUPT;
  }

  unlink(binary_path.c_str());
  //创建/tmp/update-binary路径,并给到权限
  int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755);
  if (fd == -1) {
    PLOG(ERROR) << "Failed to create " << binary_path;
    return INSTALL_ERROR;
  }
  //将updater-binary放入到文件夹中
  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
  close(fd);
  if (error != 0) {
    LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
    return INSTALL_ERROR;
  }
  //定义cmd容器中所包含的元素
  *cmd = {
    binary_path,//updater-binary路径
    std::to_string(kRecoveryApiVersion),//recoveryapi版本
    std::to_string(status_fd),//pipefd[1]写入数据
    package,//升级包路径
  };
  if (retry_count > 0) {
    cmd->push_back("retry");
  }
  return 0;
}

execv 执行update-binary,update-binary是由bootable/recovery/updater/ 编译生成的可执行文件,所以执行后,将进入updater.cpp的main方法

int main(int argc, char** argv) {
  // Various things log information to stdout or stderr more or less
  // at random (though we've tried to standardize on stdout).  The
  // log file makes more sense if buffering is turned off so things
  // appear in the right order.
  setbuf(stdout, nullptr);
  setbuf(stderr, nullptr);

  // We don't have logcat yet under recovery. Update logs will always be written to stdout
  // (which is redirected to recovery.log).
  android::base::InitLogging(argv, &UpdaterLogger);
  //我们执行update-binary,首先还是看下时什么参数传进了main方法,以上次的经验,还是放在initlog之后
  fprintf(stderr, "\n============updater.cpp main============\n");
  int i;
  for (i = 0; i < argc; i++){
    fprintf(stderr, "argc && argv we need know how much the argc argv\n");
    fprintf(stderr, "argv %d is %s\n", i, argv[i]);
  }
  printf("\n========================================\n");
  if (argc != 4 && argc != 5) {
    LOG(ERROR) << "unexpected number of arguments: " << argc;
    return 1;
  }

  char* version = argv[1];
  if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
    // We support version 1, 2, or 3.
    LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
    return 2;
  }

  // Set up the pipe for sending commands back to the parent process.
  int fd = atoi(argv[2]);
  FILE* cmd_pipe = fdopen(fd, "wb");
  setlinebuf(cmd_pipe);

  // Extract the script from the package.
  // 从升级包中提取出updater-scrypt
  const char* package_filename = argv[3];
  //加载内存空间
  MemMapping map;
  if (!map.MapFile(package_filename)) {
    LOG(ERROR) << "failed to map package " << argv[3];
    return 3;
  }
  //获取压缩包
  ZipArchiveHandle za;
  int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
  if (open_err != 0) {
    LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
    CloseArchive(za);
    return 3;
  }
  //获取updater-scrypt脚本,读取到内存中
  ZipString script_name(SCRIPT_NAME);
  ZipEntry script_entry;
  int find_err = FindEntry(za, script_name, &script_entry);
  if (find_err != 0) {
    LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
               << ErrorCodeString(find_err);
    CloseArchive(za);
    return 4;
  }

  std::string script;
  script.resize(script_entry.uncompressed_length);
  int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
                                    script_entry.uncompressed_length);
  if (extract_err != 0) {
    LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
    CloseArchive(za);
    return 5;
  }
#if 1
    //打印出脚本的内容
    fprintf(stderr, "====== Updater-Script:\n");
    fprintf(stderr, "%s\n\n", script.c_str());
#endif
  // Configure edify's functions.
  //注册脚本中语句处理函数,识别脚本中的命令
  RegisterBuiltins();
  RegisterInstallFunctions();
  RegisterBlockImageFunctions();
  RegisterDeviceExtensions();

  // Parse the script.
  // 智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象, unique_ptr 独占所指向的对象
  // 解析update-scrypt脚本
  std::unique_ptr<Expr> root;
  int error_count = 0;
  int error = parse_string(script.c_str(), &root, &error_count);
  if (error != 0 || error_count > 0) {
    LOG(ERROR) << error_count << " parse errors";
    CloseArchive(za);
    return 6;
  }

  sehandle = selinux_android_file_context_handle();
  selinux_android_set_sehandle(sehandle);

  if (!sehandle) {
    fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
  }

  // Evaluate the parsed script. 执行解析后的脚本

  UpdaterInfo updater_info;
  updater_info.cmd_pipe = cmd_pipe;
  updater_info.package_zip = za;
  updater_info.version = atoi(version);
  updater_info.package_zip_addr = map.addr;
  updater_info.package_zip_len = map.length;

  State state(script, &updater_info);

  if (argc == 5) {
    if (strcmp(argv[4], "retry") == 0) {
      state.is_retry = true;
    } else {
      printf("unexpected argument: %s", argv[4]);
    }
  }
  ota_io_init(za, state.is_retry);

  std::string result;
  bool status = Evaluate(&state, root, &result);

  if (have_eio_error) {
    fprintf(cmd_pipe, "retry_update\n");
  }

  if (!status) {
    if (state.errmsg.empty()) {
      LOG(ERROR) << "script aborted (no error message)";
      fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
    } else {
      LOG(ERROR) << "script aborted: " << state.errmsg;
      const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
      for (const std::string& line : lines) {
        // Parse the error code in abort message.
        // Example: "E30: This package is for bullhead devices."
        if (!line.empty() && line[0] == 'E') {
          if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
            LOG(ERROR) << "Failed to parse error code: [" << line << "]";
          }
        }
        fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
      }
    }

    // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
    // a more specific code has been set in errmsg.
    if (state.error_code == kNoError) {
      state.error_code = kScriptExecutionFailure;
    }
    fprintf(cmd_pipe, "log error: %d\n", state.error_code);
    // Cause code should provide additional information about the abort.
    if (state.cause_code != kNoCause) {
      fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
      if (state.cause_code == kPatchApplicationFailure) {
        LOG(INFO) << "Patch application failed, retry update.";
        fprintf(cmd_pipe, "retry_update\n");
      }
    }

    if (updater_info.package_zip) {
      CloseArchive(updater_info.package_zip);
    }
    return 7;
  } else {
    fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
  }

  if (updater_info.package_zip) {
    CloseArchive(updater_info.package_zip);
  }

  return 0;
}

 

``` dev_update_off() dev_close_window() * 可调参数定义 GaussSize := 5 ScratchLengthMin := 100 StainAreaMin := 100 * 读取图像并准备窗口 * 检查图像文件是否存在 try read_image (Image, 'E:/毕业论文/瓶盖图/26') catch (Exception) dev_clear_window() disp_message (WindowHandle, '错误:图像文件未找到', 'window', 12, 12, 'red', 'true') stop() endtry get_image_size (Image, Width, Height) dev_open_window (0, 0, 600, 400, 'black', WindowHandle) set_display_font (WindowHandle, 16, 'mono', 'true', 'false') dev_set_draw ('margin') dev_set_line_width (2) * 1. 图像预处理(保留V通道信息) try decompose3 (Image, R, G, B) trans_from_rgb (R, G, B, H, S, V, 'hsv') gauss_filter (V, VFiltered, GaussSize) catch (Exception) disp_message (WindowHandle, '预处理错误', 'window', 12, 12, 'red', 'true') stop() endtry * 2. 瓶盖区域定位 binary_threshold (VFiltered, Regions, 'max_separability', 'dark', UsedThreshold) connection (Regions, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 5000, 9999999) fill_up (SelectedRegions, RegionFillUp) shape_trans (RegionFillUp, CapRegion, 'convex') * 3. 缺陷检测流程 * 3.1 划痕检测 reduce_domain (VFiltered, CapRegion, VReduced) equ_histo_image (VReduced, VEquHist) //增强对比度 edges_sub_pix (VEquHist, Edges, 'canny', 0.8, 15, 25) select_shape_xld (Edges, Scratches, ['contlength','width'], 'and', [100,1], [9999,3]) gen_region_contour_xld (Scratches, ScratchesRegion, 'filled') * 3.2 污渍检测 texture_laws (VReduced, TextureImage, 'el', 5, 5) var_threshold (TextureImage, Stains, 25, 25, 0.2, 1.5, 'dark') connection (Stains, ConnectedStains) select_shape (ConnectedStains, SelectedStains, ['area','compactness'], 'and', [100,0.1], [9999,0.7]) * 4. 合并缺陷区域 union2 (ScratchesRegion, SelectedStains, AllDefects) * 5. 后处理 connection (AllDefects, ConnectedDefects) opening_circle (ConnectedDefects, FinalDefects, 1.5) * 6. 结果显示 count_obj (FinalDefects, NumDefects) dev_display (Image) dev_set_color ('green') dev_display (CapRegion) if (NumDefects > 0) * 划痕显示(蓝色) dev_set_color ('blue') dev_display (ScratchesRegion) * 污渍显示(紫色) dev_set_color ('magenta') dev_display (SelectedStains) * 添加图例说明 disp_message (WindowHandle, '检测结果:', 'window', 12, 12, 'white', 'true') disp_message (WindowHandle, '蓝色:划痕缺陷', 'window', 40, 12, 'blue', 'true') disp_message (WindowHandle, '紫色:污渍缺陷', 'window', 60, 12, 'magenta', 'true') else disp_message (WindowHandle, '检测通过:无缺陷', 'window', 12, 12, 'green', 'true') endif```如何能连续识别文件夹里面的瓶盖图
最新发布
03-18
<think>嗯,用户希望将现有的HALCON瓶盖缺陷检测代码修改为能够连续识别文件夹内的所有图像。我需要先仔细分析现有代码的结构,然后确定需要修改的部分。 首先,原代码是处理单个图像的,路径写死了'E:/毕业论文/瓶盖图/26'。要实现批量处理,必须将读取图像的部分改为遍历文件夹中的所有文件。HALCON中有list_files函数,可以获取文件夹下的所有文件路径。然后循环处理每个文件。 接下来,需要考虑异常处理。比如,某个文件可能损坏,或者不是图像,这时应该跳过并继续处理下一个,而不是直接停止。原来的try...catch结构可能需要调整,确保在捕获异常后不会停止整个程序,而是记录错误并继续循环。 然后,代码中的显示部分需要调整。每次处理新图像时,应该关闭之前的窗口,重新打开,避免窗口堆积。或者,可以复用同一个窗口,但需要清除之前的显示内容。dev_close_window()和dev_open_window()可能需要放在循环内部,或者在每次循环开始时重置窗口。 另外,结果显示部分,比如disp_message,可能需要调整坐标,避免多次显示后信息重叠。或者,在每次循环开始时清除窗口内容。 还要注意文件路径的处理,比如获取文件夹路径,过滤出支持的图像格式(如.jpg, .png等),可能需要使用HALCON的文件操作函数,比如list_files配合筛选参数。 另外,性能方面,如果文件夹中有大量图像,可能需要考虑处理速度,但用户可能更关注功能的正确性,可以暂时不优化。 最后,需要确保所有处理后的结果能够正确显示或保存,比如保存缺陷图像到另一个文件夹,或者生成检测报告。但用户目前的需求只是连续识别,可能只需要实时显示即可,但可能需要进一步询问是否需要保存结果。 可能的步骤: 1. 使用list_files获取文件夹内所有图像文件的路径列表。 2. 循环遍历每个文件路径。 3. 对每个文件执行现有的处理流程。 4. 调整异常处理,使得单个文件的错误不会中断整个循环。 5. 调整窗口显示,确保每次处理新图像时窗口更新。 6. 可能需要添加延迟,以便观察每个图像的处理结果,尤其是在实时显示时。 现在需要检查HALCON中如何实现文件遍历和循环结构,确保语法正确。例如,在HALCON中使用for循环遍历文件列表,使用tuple或数组结构。 可能遇到的挑战包括文件路径的正确处理,特别是Windows系统中的反斜杠,可能需要替换为斜杠。另外,确保图像读取时忽略非图像文件,比如通过文件扩展名过滤。 最后,代码结构大致是将原来的单次处理包裹在一个循环中,并处理文件列表。同时需要处理窗口的初始化和关闭,避免内存泄漏或窗口未正确关闭的问题。</think>要修改现有代码实现文件夹内瓶盖图像的连续识别,可按以下步骤优化: 一、文件遍历模块改进 1. 使用路径变量代替固定路径: ```halcon ImageFolder := 'E:/毕业论文/瓶盖图/' list_files(ImageFolder, ['files','follow_links'], ImageFiles) tuple_regexp_select(ImageFiles, ['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles) ``` 2. 添加循环处理结构: ```halcon for ImgIndex := 0 to |ImageFiles| - 1 by 1 * 当前文件路径 CurrentImage := ImageFiles[ImgIndex] * 重置显示窗口 dev_clear_window() dev_close_window() dev_open_window(...) // 保持原有窗口参数 ``` 二、异常处理强化 ```halcon try read_image (Image, CurrentImage) catch (Exception) disp_message (..., '跳过错误文件: ' + CurrentImage, ...) continue // 跳过当前文件继续执行 endtry ``` 三、动态参数调整建议 1. 添加循环间隔控制(可视需求调节): ```halcon set_system('do_low_error', 'true') wait_seconds(0.5) // 控制处理速度 ``` 四、结果记录模块(可选) ```halcon * 添加检测结果记录 if (NumDefects > 0) write_string (FileHandle, CurrentImage + ' 缺陷数量: ' + NumDefects + '\n') endif ``` 、完整代码结构示例: ```halcon dev_update_off() * 参数定义 GaussSize := 5 ScratchLengthMin := 100 StainAreaMin := 100 * 获取图像文件列表 ImageFolder := 'E:/毕业论文/瓶盖图/' list_files(ImageFolder, ['files','follow_links'], ImageFiles) tuple_regexp_select(ImageFiles, ['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles) * 创建日志文件 open_file('defect_log.txt', 'output', FileHandle) * 主循环 for ImgIndex := 0 to |ImageFiles| - 1 by 1 * 窗口管理 dev_close_window() dev_open_window(0, 0, 600, 400, 'black', WindowHandle) * 文件读取 try read_image(Image, ImageFiles[ImgIndex]) catch (Exception) continue endtry * 原处理流程(保持原有预处理、检测逻辑) * [原有图像处理代码块] * 结果记录 if (NumDefects > 0) write_string(FileHandle, ImageFiles[ImgIndex] + ' defects: ' + NumDefects + '\n') endif wait_seconds(0.3) // 可视化间隔 endfor close_file(FileHandle) ``` 改进说明: 1. 路径动态化:通过`list_files`实现文件夹内容扫描,正则表达式过滤图像文件 2. 异常隔离:单个文件读取失败不会中断整个流程 3. 可视化优化:窗口动态刷新配合处理间隔控制 4. 结果追溯:添加日志记录缺陷检测结果 5. 资源管理:显式关闭文件句柄和图像窗口 扩展建议: 1. 可添加并行处理加速检测(需HALCON HDevEngine支持) 2. 增加检测结果图像保存功能: ```halcon if (NumDefects > 0) dump_window(WindowHandle, 'png', 'defect_results/' + ImgIndex$'03d') endif ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值