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可以访问该表,但是修改事会遇到阻塞等待,只有等到事务一释放锁后…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...