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
相关文章:

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

中国各城市土地利用类型(城市功能)数据集(shp)
中国各城市土地利用类型(城市功能)数据集 时间:2018年 全国范围的城市用地类型数据(居住/商业/交通用地等共计11类) 分类:居住用地、商业用地、工业用地、医疗设施用地、体育文化设施用地、交通场站用地、绿地等用地类型 含城市编码、一级分类5个、二级分类11个 数据按…...

Linux网络编程:数据链路层
目录 一. 数据链路层概述 二. 以太网 2.1 以太网的概念 2.2 以太网数据帧 2.3 对于MAC地址的认识 2.4 数据碰撞问题 三. MTU和MSS 3.1 什么是MTU 3.2 MTU对UDP的影响 3.3 MTU对TCP的影响(MSS的概念) 四. ARP协议 4.1 ARP协议的作用 4.2 ARP数…...
python 线程 超时时间
python 线程 超时时间_mob649e815f0f18的技术博客_51CTO博客...
LeetCode:274. H 指数、275. H 指数 II(C++)
目录 274. H 指数 题目描述: 实现代码与解析: 排序暴力 275. H 指数 II 题目描述: 实现代码与解析: 二分 比较简单,不再写解析,注意二分的时候,r指针为n,含义为个数…...
多线程及锁
1.lock锁和synchronized锁的区别。 1:Synchronized 是Java的一个关键字,而Lock是java.util.concurrent.Locks 包下的一个接口; 2:Synchronized 使用过后,会自动释放锁,而Lock需要手动上锁、手动释放锁&am…...

C++ 写一个Data类的注意问题
Data类 声明和定义分离的一些问题 声明里面我们不带缺省参数,定义我们给缺省参数,如下面两段代码: Data.h#pragma once #include<iostream> using namespace std; class Data { public:Data(int year,int month,int day);private:in…...

postman做接口测试
之前搞自动化接口测试,由于接口的特性,要验证接口返回xml中的数据,所以没找到合适的轮子,就自己用requests造了个轮子,用着也还行,不过就是case管理有些麻烦,近几天又回头看了看postman也可以玩…...
hdlbits系列verilog解答(always块)-29
文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 由于数字电路由用网线连接的逻辑门组成,因此任何电路都可以表示为模块和赋值语句的某种组合。然而,有时这不是描述电路的最方便方式。过程procedure(其中 always 的块就是一个示例)提供了描述电路的替代语法…...

uniapp实现瀑布流
首先我们要先了解什么是瀑布流: 瀑布流(Waterfall Flow)是一种常见的网页布局方式,也被称为瀑布式布局或砌砖式布局。它通常用于展示图片、博客文章、商品等多个不同大小和高度的元素。 瀑布流布局的特点是每个元素按照从上到下…...

15. 机器学习 - 支持向量机
Hi, 你好。我是茶桁。 逻辑回归预测心脏病 在本节课开始呢,我给大家一份逻辑回归的练习,利用下面这个数据集做了一次逻辑回归预测心脏病的练习。 本次练习的代码在「茶桁的AI秘籍」在Github上的代码库内,数据集的获取在文末。这样做是因为我…...
如何根据进程号查询服务的端口号
ps -ef | grep nacos ps -ef | grep nacos 命令是用于查找系统中所有包含 "nacos" 关键字的进程。这个命令的含义如下: ps: 这是一个用于显示当前正在运行的进程的命令。 -ef: 这两个选项一起使用,表示显示所有进程的详细信息。 -e 选项表示显…...
2.10、自定义量化优化过程
introduction 如何自定义量化优化过程,以及如何手动调用优化过程 code from typing import Callable, Iterableimport torch import torchvision from ppq import QuantizationSettingFactory, TargetPlatform from ppq.api import (ENABLE_CUDA_KERNEL, Quantiz…...
MySQL如何添加自定义函数
深入MySQL:学习如何添加自定义函数 MySQL 是一种流行的开源关系型数据库管理系统,它支持很多内置函数来完成各种操作。不过有时候这些内置函数无法满足我们的需求,这时候就需要自定义函数了。在 MySQL 中,可以通过编写自定义函数…...

超融合数据库:解锁全场景数据价值的钥匙
前言 近日,四维纵横对外官宣已完成上亿元 B 轮融资。作为超融合数据库理念的提出者,三年来 YMatrix 持续在超融合数据库领域中保持精进与迭代,对于超融合数据库在行业、场景中的应用和理解也更为深刻。 本篇文章,我们将基于 YMa…...

Pap.er for Mac:高清壁纸应用打造你的专属视觉盛宴
在浩瀚的互联网海洋中,你是否曾为寻找一张心仪的高清壁纸而烦恼?或者是在大量的壁纸应用中感到困扰,不知道哪一个能满足你的需求?今天,我要向你介绍的,是一款独特的5K高清壁纸应用——Pap.er for Mac。 Pa…...

AI:46-基于深度学习的垃圾邮件识别
🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…...

【骑行贝丘渔场】一场与海的邂逅,一段难忘的旅程
在这个渐凉的秋日,我们校长骑行队一行人骑着自行车,从大观公园门口出发,开始了一段别开生面的海滩之旅。沿途穿越草海隧道湿地公园、迎海路、海埂公园西门(第二集合点)、宝丰湿地公园、斗南湿地公园、蓝光城࿰…...

消息中间件——RabbitMQ(一)Windows/Linux环境搭建(完整版)
前言 最近在学习消息中间件——RabbitMQ,打算把这个学习过程记录下来。此章主要介绍环境搭建。此次主要是单机搭建(条件有限),包括在Windows、Linux环境下的搭建,以及RabbitMQ的监控平台搭建。 环境准备 在搭建Rabb…...
Mysql 表读锁与表写锁
表读锁 加锁:lock table table_name read 释放锁:unlock tables 当事务一用表读锁锁住某张表后, 1.事务一必须释放表读锁才能访问其他表 2.期间事务2可以访问该表,但是修改事会遇到阻塞等待,只有等到事务一释放锁后…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...