openpnp - src - 配置文件载入过程的初步分析

文章目录

    • openpnp - src - 配置文件载入过程的初步分析
    • 概述
    • 笔记
    • 自己编译用的git版本
    • 报错截图
    • 问题1 - 怎么在调试状态下, 定位到抛异常的第一现场?
    • 结合单步调试找到的现场, 来分析报错的原因
    • openpnp配置文件读取的流程
    • END

openpnp - src - 配置文件载入过程的初步分析

概述

从openpnp - dev - 2022_0801那天最后的代码, 编译除了一份可执行程序给自己用.
用官方打包的发布版 dev - 2022_0801运行, 居然读取我自己版本的配置文件失败.
将自己打包的那份dev-2022_0801, 再将版本退一个版本, 也会读取配置文件失败. 估计官方打包没用dev-2022_0801那天的最后版本.

通过git版本比对, 结合报错信息, 大概知道是新版本有个类, 添加了一个新属性, 导致旧版本读取不了新版本增加的属性时, 因为没有那个旧类没有那个新增加的属性, 导致抛出异常.

作为新手, 想弄清为啥读取配置文件报错, 错在哪里. 顺便看一下openpnp序列化配置文件的过程. 多一些了解, 总是没错的…

笔记

自己编译用的git版本

在这里插入图片描述
不太理解为啥官方打包的版本是2022/8/1下午的时间, 但是却没采用2022/8/1那天最新的实现. 而且提交时间只差了3分钟, 还是同一个人提交的, 迷惑…

编译openpnp工程的过程已经做了笔记(openpnp - 软件调试环境搭建).

报错截图

先用官方版本的dev-2022/8/1的发布包标定的设备.

后来发现openpnp有点bug, 修正了, 此时正常用, 但是配置文件被新版本(比官方版本晚3分钟的git版本, 修正后, 重新编译)改了, machine.xml中的视觉方案类中多了一个类属性.

偶然用官方发布的dev-2022_0801实验, 才发现报错如下:
在这里插入图片描述
根据报错提示, 可以知道 machine.xml中有个属性 settle-test-move-mm 在类org.openpnp.machine.referene.solutions.VisionSolutions中没有, 导致了抛异常报错.

看到这个报错后, 有2个想法:

  • 具体报错点在哪里? (等以后出现版本和配置文件不匹配时, 可以迅速定位到发生异常的类, 还可以知道报错前, 哪里是抛异常的第一现场?)
  • openpnp配置文件有4个xml, 这些配置文件读取的流程大致是从哪里到哪里?

用了一天, 将上面2个问题搞定了. 心里舒坦多了.

问题1 - 怎么在调试状态下, 定位到抛异常的第一现场?

我是第一次调试这个问题, 不断的下条件断点. 最后可以2步就到达报错现场. 如果不设置条件断点, 那基本就是不可调试的.

在这里插入图片描述
在这里插入图片描述
F8步过loadMachine(file), 如果没有其他条件断点, 就会直接进入437行的抛异常实现, 就会看到报错对话框.
F7步入loadMachine(file), 先F8, 如果哪步会导致进入437行报错断点, 再F7那个函数. 结合条件断点, 就能定位到抛异常的第一现场.
然后 CTRL + ALT + 向左的箭头, 就可以回到最后一次F8的代码实现. 然后停掉程序, 看看是不是第一现场, F7, F8, 直到找到第一现场.

当断住后, 如果不是我们要的调试代码, 按F9, 让程序继续跑, 命中我们后续的断点继续调试. 如果确定断点没用了, 就在一个断点上右击, 进入断点编辑器, 禁止掉不用的断点(不用删除, 可能找调试痕迹还有用).

在这里插入图片描述
这里就是找到的抛异常的第一现场.
2个条件断点都一样, 如下:

((null != name) && (19 == name.length()) && (0x73 == name.value[0]))

这个条件断点, 是找name == “visual-solutions”, 为了简单, 值判断了name的长度和第一个字符, 已经可以正确断住了. 如果只判断一个字符值不好使, 那就再挑一个字符值, 看情况, 条件写的越少越好.

在IDEA中下字符串的条件断点的方法已经做了笔记(java - IDEA IDE - 设置字符串断点).

因为报错的第一现场是在第三方jar包中, 只读的无法添加调试代码, 所以下条件断点能捕获到调试点就行.

结合单步调试找到的现场, 来分析报错的原因

第一现场的代码如下:

   private void readAttribute(InputNode node, Object source, Section section, LabelMap map) throws Exception {String name = node.getName(); // 取节点名称String path = section.getAttribute(name); // 取属性的path// map这里是类实际的属性值的集合// path是xml中指定的这个类的属性名称Label label = map.getLabel(path); // 拿属性的path去取属性的值if(label == null) {// 这里是没取到属性的值, 也就是旧版本openpnp读取新版本配置文件, 因为具体的类没有这个属性, 自然无法将xml中规定的类属性设置到类的到属性成员中.// 这里就是抛异常的第一现场Position line = node.getPosition(); // 报错的行数, 这里取的值不准, 没有参考价值. 文件是应该包含该属性的类, 行数(line)根本不对. 不怪IDEA, 因为这个属性就不存在, 用属性的代码行数自然也不对.Class expect = context.getType(type, source); // 好像是发生错误的类名称// 如果map是要求严格匹配(类的属性和xml中必须严格对应), 缓存也是要求严格匹配的, 就抛出异常报错.if(map.isStrict(context) && revision.isEqual()) {   // 然后就进入这里, 抛异常, 异常的内容就在报错框中被我们看到           throw new AttributeException("Attribute '%s' does not have a match in %s at %s", path, expect, line);}            } else {readInstance(node, source, label);}         }

openpnp配置文件读取的流程

只看用户空间的代码(主干函数), 进了第三方库的实现就不看了.

从main函数开始
D:\my_openpnp\openpnp_github\src\main\java\org\openpnp\Main.java

    public static void main(String[] args) {monkeyPatchBeansBinding();for (String s : args) {if (s.equals("--version")) {System.out.println(getVersion());System.exit(0);}}// http://developer.apple.com/library/mac/#documentation/Java/Conceptual/Java14Development/07-NativePlatformIntegration/NativePlatformIntegration.html#//apple_ref/doc/uid/TP40001909-212952-TPXREF134System.setProperty("apple.laf.useScreenMenuBar", "true");try {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}catch (Exception e) {throw new Error(e);}File configurationDirectory = new File(System.getProperty("user.home"));configurationDirectory = new File(configurationDirectory, ".openpnp2");if (System.getProperty("configDir") != null) {configurationDirectory = new File(System.getProperty("configDir"));}configurationDirectory.mkdirs();configureLogging(configurationDirectory);Configuration.initialize(configurationDirectory);final Configuration configuration = Configuration.get();Locale.setDefault(Configuration.get().getLocale());ThemeInfo theme = configuration.getThemeInfo();new ThemeSettingsPanel().setTheme(theme, configuration.getFontSize(), configuration.isAlternateRows());ThemeDialog.getInstance().setOldTheme(theme);EventQueue.invokeLater(new Runnable() {public void run() {try {MainFrame frame = new MainFrame(configuration); // 进入MainFrame实现读配置frame.setVisible(true);Logger.info(String.format("Bienvenue, Bienvenido, Willkommen, Hello, Namaskar, Welkom, Bonjour to OpenPnP version %s.", Main.getVersion()));configuration.getScripting().on("Startup", null);}catch (Exception e) {e.printStackTrace();}}});}
}

D:\my_openpnp\openpnp_github\src\main\java\org\openpnp\gui\MainFrame.java

public MainFrame(Configuration configuration) {
// 巨大的一个函数, 只看相关实现
// ...registerBoardImporters();addComponentListener(componentListener);boolean configurationLoaded = false;while (!configurationLoaded) {try {configuration.load(); // 这里载入配置文件(4个.xml), 如果配置文件过不去, 就会抛一场.scriptFileWatcher = new ScriptFileWatcher(configuration.getScripting());scriptFileWatcher.setMenu(mnScripts);if (Configuration.get().getMachine().getProperty("Welcome2_0_Dialog_Shown") == null) {Welcome2_0Dialog dialog = new Welcome2_0Dialog(this);dialog.setSize(750, 550);dialog.setLocationRelativeTo(null);dialog.setModal(true);dialog.setVisible(true);Configuration.get().getMachine().setProperty("Welcome2_0_Dialog_Shown", true);}configurationLoaded = true;    }catch (Exception e) {// 这里就是用户看到报错提示框的地方e.printStackTrace();if (!MessageBoxes.errorBoxWithRetry(this, "Configuration Load Error", //$NON-NLS-1$"There was a problem loading the configuration. The reason was:<br/><br/>" //$NON-NLS-1$+ e.getMessage() + "<br/><br/>" //$NON-NLS-1$+ "Please check your configuration files and try again. They are located at: " //$NON-NLS-1$+ configuration.getConfigurationDirectory().getAbsolutePath()+ "<br/><br/>" //$NON-NLS-1$+ "If you would like to start with a fresh configuration, just delete the entire directory at the location above.<br/><br/>" //$NON-NLS-1$+ "Retry loading (else openpnp will exit) ?")) { //$NON-NLS-1$System.exit(1);}}}splitWindows();}
}

D:\my_openpnp\openpnp_github\src\main\java\org\openpnp\model\Configuration.java

	// 用户空间读取配置的函数// .xml和具体的类是对应的, 就是序列化xml文件到类的属性public synchronized void load() throws Exception {boolean forceSave = false;boolean overrideUserConfig = Boolean.getBoolean("overrideUserConfig");// 1. 读取packages.xml到具体类的属性try {File file = new File(configurationDirectory, "packages.xml");if (overrideUserConfig || !file.exists()) {Logger.info("No packages.xml found in configuration directory, loading defaults.");file = File.createTempFile("packages", "xml");FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/packages.xml"), file);forceSave = true;}loadPackages(file);}catch (Exception e) {String message = e.getMessage();if (e.getCause() != null && e.getCause().getMessage() != null) {message = e.getCause().getMessage();}throw new Exception("Error while reading packages.xml (" + message + ")", e);}// 2. 读取parts.xml到具体类的属性try {File file = new File(configurationDirectory, "parts.xml");if (overrideUserConfig || !file.exists()) {Logger.info("No parts.xml found in configuration directory, loading defaults.");file = File.createTempFile("parts", "xml");FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/parts.xml"), file);forceSave = true;}loadParts(file);}catch (Exception e) {String message = e.getMessage();if (e.getCause() != null && e.getCause().getMessage() != null) {message = e.getCause().getMessage();}throw new Exception("Error while reading parts.xml (" + message + ")", e);}// 3. 读取vision-settings.xml到具体类的属性try {File file = new File(configurationDirectory, "vision-settings.xml");if (overrideUserConfig || !file.exists()) {Logger.info("No vision-settings.xml found in configuration directory, loading defaults.");file = File.createTempFile("visionSettings", "xml");FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/vision-settings.xml"), file);forceSave = true;}loadVisionSettings(file);}catch (Exception e) {String message = e.getMessage();if (e.getCause() != null && e.getCause().getMessage() != null) {message = e.getCause().getMessage();}throw new Exception("Error while reading vision-settings.xml (" + message + ")", e);}// 4. 读取machine.xml到具体类的属性// 这次调试, 就能看到是在读取machine.xml配置文件时, 抛异常.try {File file = new File(configurationDirectory, "machine.xml");if (overrideUserConfig || !file.exists()) {Logger.info("No machine.xml found in configuration directory, loading defaults.");file = File.createTempFile("machine", "xml");FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/machine.xml"), file);forceSave = true;}loadMachine(file); // 这里具体都配置文件}catch (Exception e) {String message = e.getMessage();if (e.getCause() != null && e.getCause().getMessage() != null) {message = e.getCause().getMessage();}throw new Exception("Error while reading machine.xml (" + message + ")", e);}loaded = true;// Tell all listeners the configuration is loaded. Use a snapshot of the list in order to tolerate new// listener additions that may happen through object migration.for (ConfigurationListener listener : new ArrayList<>(listeners)) {listener.configurationLoaded(this);}if (forceSave) {Logger.info("Defaults were loaded. Saving to configuration directory.");configurationDirectory.mkdirs();save();}for (ConfigurationListener listener : listeners) {listener.configurationComplete(this);}}// 下面是保存配置文件的函数public synchronized void save() throws Exception {LocalDateTime now = LocalDateTime.now();
    private void loadMachine(File file) throws Exception {Serializer serializer = createSerializer();// serializer.read 已经离开用户代码, 去了第三方库中.// 如果不是调试代码, 就不用去看了.MachineConfigurationHolder holder = serializer.read(MachineConfigurationHolder.class, file);machine = holder.machine;}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/174164.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工厂模式(Factory)

简单工厂模式 简单工厂模式又叫静态工厂模式&#xff0c;顾名思义&#xff0c;它是用来实例化目标类的静态类。下面我主要通过一个简单的实例说明简单工厂及其优点。 工厂方法模式 工厂方法模式和简单工厂模式在结构上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类…

npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

1 bug描述 使用vscode执行npm run dev指令时出现 “npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次 “ 的错误提示&#xff0c;原因是系统里没有安装n…

【机器学习】loss损失讨论

大纲 验证集loss上升&#xff0c;准确率也上升&#xff08;即将overfitting&#xff1f;&#xff09;训练集loss一定为要为0吗 Q1. 验证集loss上升&#xff0c;准确率也上升 随着置信度的增加&#xff0c;一小部分点的预测结果是错误的&#xff08;log lik 给出了指数级的惩…

微信小程序设计之页面文件pages

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

C++笔记-RTTR编译安装简单使用

这里以Linux为例&#xff0c;我使用的机器的gcc版本是4.9.2 使用的RTTR的版本是0.9.5 编译&安装 首先在官网将rttr的0.9.5版本下载下来。 Home |RTTR 按照官方的安装流程&#xff1a; 但这里可能会出现一个问题&#xff1a; 按照解答&#xff0c;切换成root用户&#x…

【设计模式】第13节:结构型模式之“享元模式”

一、简介 所谓“享元”&#xff0c;顾名思义就是被共享的单元。享元模式的意图是复用对象&#xff0c;节省内存&#xff0c;前提是享元对象是不可变对象。 实现&#xff1a;通过工厂模式&#xff0c;在工厂类中&#xff0c;通过一个Map或者List来缓存已经创建好的享元对象&am…

文件夹比较工具怎么用 对比两个文件夹找出多余的文件

在日常工作中&#xff0c;经常会接触到大量的文件&#xff0c;长时间堆积&#xff0c;文件夹会越来越多&#xff0c;从而导致文件重复&#xff0c;如果想要找出想要的文件会比较麻烦&#xff0c;那么你知道应该怎么来查找吗&#xff1f;下面就让我们来学习一下文件夹比较工具怎…

香港服务器运行不正常原因简析

​  网站在线业务的部署需要服务器的存在。于我们而言&#xff0c;租用正规服务商(正规机房)的服务器&#xff0c;一般情况下是会很少出现问题。但&#xff0c;要知道&#xff0c;再稳定的服务器也有出现问题的时候&#xff0c;香港服务器也不例外&#xff0c;而且恰恰这个原…

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matla…

C语言基础简述(一)

目录 1. 标准输入输出库 2. 定义变量 3. 关于基本数据类型 3.1 计算机如何存储数据 3.2 基本数据类型 3.2.1 数值类型 3.2.1.1 整数类型 3.2.1.2 浮点数类型 3.2.2 字符类型 3.2.2.1 ASCII表 4. 进制数之间的转换 4.1 十进制整数和二进制之间的相互转换 5. C语言运…

椭圆曲线在SM2加解密中的应用(三)

一、SM2加密运算 1.1加密原始数据 SM2加密运算首先是用户A对数据加密&#xff0c;用户A拥有原始数据 椭圆曲线系统参数长度为klen比特的消息M公钥Pb 椭圆曲线系统参数&#xff0c;已经在 椭圆曲线参数&#xff08;二&#xff09;中详细介绍&#xff1b;M就是需要加密消息&am…

Ajax学习笔记第三天

做决定之前仔细考虑&#xff0c;一旦作了决定就要勇往直前、坚持到底&#xff01; 【1 ikunGG邮箱注册】 整个流程展示&#xff1a; 1.文件目录 2.页面效果展示及代码 mysql数据库中的初始表 2.1 主页 09.html:里面代码部分解释 display: inline-block; 让块元素h1变成行内…

前端移动web高级详细解析三

模拟移动设备&#xff0c;方便查看页面效果 屏幕分辨率 分类&#xff1a; 物理分辨率&#xff1a;硬件分辨率&#xff08;出厂设置&#xff09; 逻辑分辨率&#xff1a;软件 / 驱动设置 结论&#xff1a;制作网页参考 逻辑分辨率 视口 作用&#xff1a;显示 HTML 网页的区…

winodos下使用VS2022编译eclipse-paho.mqtt.c并演示简单使用的 demo

本文演示C语言如何使用eclipse-paho.mqtt.c库&#xff0c;包含自行编译库的步骤或者下载编译好的文件。 1.下载paho.mqtt.c库源码&#xff08;zip 文件&#xff09; 到官网选择C版本的paho源码进行下载 Eclipse Paho | The Eclipse Foundation 或者到下述连接下载 Releases ec…

SpringBoot面试题8:运行 Spring Boot 有哪几种方式?Spring Boot 需要独立的容器运行吗?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:运行 Spring Boot 有哪几种方式? 运行Spring Boot应用有多种方式,具体取决于你的需求和环境。以下是几种常见的运行Spring Boot应用的方式: 使…

论文阅读——GPT3

来自论文&#xff1a;Language Models are Few-Shot Learners Arxiv&#xff1a;https://arxiv.org/abs/2005.14165v2 记录下一些概念等。&#xff0c;没有太多细节。 预训练LM尽管任务无关&#xff0c;但是要达到好的效果仍然需要在特定数据集或任务上微调。因此需要消除这个…

Java毕业设计 SpringBoot 新能源充电桩管理系统

Java毕业设计 SpringBoot 新能源充电桩管理系统 SpringBoot 新能源充电桩管理系统 功能介绍 管理员 登录 验证码 注册 系统用户管理 普通用户管理 通知公告管理 留言管理 充电站管理 充电桩管理 充电桩预约 充电管理 订单管理 修改密码 普通用户 登录 修改个人资料 通知公告…

【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5709-Apples Prologue / 苹果和虫子【入门2分支结构】&#x1f30f;题目描述&am…

解决MySQL大版本升级导致.Net(C#)程序连接报错问题

数据库版本从MySQL 5.7.21 升级到 MySQL8.0.21 数据升级完成后&#xff0c;直接修改程序的数据库连接配置信息 <connectionStrings> <add name"myConnectionString" connectionString"server192.168.31.200;uidapp;pwdFgTDkn0q!75;databasemail;&q…

C#,数值计算——分类与推理Svmlinkernel的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Svmlinkernel : Svmgenkernel { public int n { get; set; } public double[] mu { get; set; } public Svmlinkernel(double[,] ddata, double[] yy) : base(yy, ddata) …