【CSDN独家公开】Python解析.SchDoc格式文件转换为json文件

前情提要

因工作需求,需要解析.SchDoc格式文件,提取文本和位置关系,通常方式是转换为图片或PDF,再进行OCR,但是这样识别精度太低了
Github找了好些项目,都不支持

PyAltium不支持

https://github.com/pluots/PyAltium
在这里插入图片描述

altium不支持python

https://github.com/pluots/altium
在这里插入图片描述

ChatGPT更是胡言乱语

这里就不放图片了

话不多说,上代码

改动自https://github.com/a3ng7n/Altium-Schematic-Parser
原项目直接运行会报错,而且没有支持中文
首先安装olefile

pip install olefile

改一下你的文件地址,然后运行以下代码

import argparse, textwrap
import olefile
import re
import json
import copy
import math
import logging
import os
import codecs
logging.basicConfig()
lg = logging.getLogger(__name__)def parse(input, format, **kwargs):fullPath = inputblah = olefile.OleFileIO(fullPath)stream = blah.openstream('FileHeader')# split binary stream into lines using a repeated 5 byte signaturepattern = re.compile(b'.{3}\x00\x00\|')lines = pattern.split(stream.read()[5:-1]) # lopping off first 4 bytes, and last byte, since they don't seem to matter?schematic = {}datums = []# loop through every "line" and parse each into a dictionaryfor line in lines:datum = {}pairs = line.split(b"|")for pair in pairs:data = pair.split(b"=")if len(data) == 2:datum[data[0].decode()] = data[1].decode('utf-8', 'ignore')datums.append(datum)# separate out the header dictionary from the "records" dictionariesschematic["header"] = [x for x in datums if 'HEADER' in x.keys()]schematic["records"] = [x for x in datums if 'RECORD' in x.keys()]hierarchy_schematic = determine_hierarchy(schematic)if format == 'all_hierarchy':schematic = hierarchy_schematicelif format == 'parts_list':schematic = determine_parts_list(hierarchy_schematic)elif format == 'net_list':schematic = determine_net_list(hierarchy_schematic)return schematicdef determine_hierarchy(schematic):"""Convert a dict containing a flat list of recordsinto a dict of records in a hierarchy:param schematic: dict with 'header' and 'records' populated:return: the input dict with 'records' assembled into parent/child hierarchy"""# prep a scratchpad copy of records to build hierarchy fromrecords_copy = copy.deepcopy(schematic["records"])schematic["hierarchy"] = []# loop through all "records" and organize them into owner/childrenfor i, current in enumerate(records_copy):current['index'] = is = current.get("OWNERINDEX")if s == None:schematic["hierarchy"].append(current)else:ownerIndex = int(s)owner = records_copy[ownerIndex]if (owner.get("children") == None):owner["children"] = []owner["children"].append(current)schematic["records"] = schematic["hierarchy"]schematic.pop("hierarchy", None)return schematicdef determine_parts_list(schematic):parts_list = {"records": [ record for record in schematic["records"] if record["RECORD"] == "1" ]}return parts_listdef determine_net_list(schematic):_, wires = find_record(schematic, key="RECORD", value="27")_, pins = find_record(schematic, key="RECORD", value="2")_, labels = find_record(schematic, key="RECORD", value="25")_, power_ports = find_record(schematic, key="RECORD", value="17")devices = wires + pins + labels + power_portsp = re.compile('^(?P<prefix>X)(?P<index>\d+)$')for device in devices:# if a Pin, do some fancy geometry mathif device["RECORD"] == "2":rotation = (int(device["PINCONGLOMERATE"]) & 0x03) * 90device['coords'] = [[int(int(device['LOCATION.X']) + math.cos(rotation / 180 * math.pi) * int(device['PINLENGTH'])),int(int(device['LOCATION.Y']) + math.sin(rotation / 180 * math.pi) * int(device['PINLENGTH']))]]# if a Wire, follow inconsistent location key names (X1 vs LOCATION.X, etc..)elif device["RECORD"] == "27":coord_name_matches = [x for x in [p.match(key) for key in device.keys()] if x]device['coords'] = [ ( int(device['X' + match.group('index')]) , int(device['Y' + match.group('index')]) )for match in coord_name_matches ]# everything else, just convert the location values to intselse:device['coords'] = [(int(device['LOCATION.X']), int(device['LOCATION.Y']))]nets = []for device in devices:if device["index"] not in [d['index'] for net in nets for d in net['devices']]:net = {'name': None,'devices': find_connected_wires(device, devices, [], schematic)}nets.append(net)for net in nets:net['devices'].sort(key=lambda k: k['index'])if not net['name']:net['name'] = next(iter(d['TEXT'] for d in net['devices'] if ((d['RECORD'] == '17') or (d['RECORD'] == '25'))), None)if not net['name']:naming_pin = next(iter(d for d in net['devices'] if d['RECORD'] == '2'), None)parent = next(iter(find_record(schematic, key="index", value=int(naming_pin['OWNERINDEX']))[1]), None) if naming_pin else Nonenet['name'] = next(iter('Net' + r['TEXT'] for r in parent['children'] if (r['RECORD'] == '34')), None) if parent else Noneschematic["nets"] = netsreturn schematicdef find_record(schematic, key, value, record=None, visited=None, found=None):lg.debug("finding records where: {0} = {1}".format(key, value))if visited == None:visited = []if found == None:found = []if record == None:for record in schematic['records']:visited, found = find_record(schematic, key, value, record=record, visited=visited, found=found)else:if record['index'] not in [r['index'] for r in visited]:visited.append(record)if key in record.keys():if record[key] == value:found.append(record)if "children" in record.keys():for child_record in record["children"]:visited, found = find_record(schematic, key, value, record=child_record, visited=visited, found=found)return visited, founddef find_connected_wires(wire, devices, visited, schematic):neighbors = find_neighbors(wire, devices, schematic)lg.debug('entering: {0}'.format(wire['index']))if wire['index'] not in [w['index'] for w in visited]:lg.debug('adding: {0} to {1}'.format(wire['index'], [w['index'] for w in visited]))visited.append(wire)for neighbor in neighbors:lg.debug('trying: {0} of {1}'.format(neighbor['index'], [x['index'] for x in neighbors]))visited = find_connected_wires(neighbor, devices, visited, schematic)lg.debug('visited = {0}'.format([w['index'] for w in visited]))else:lg.debug('skipping: {0} already in list {1}'.format(wire['index'], [w['index'] for w in visited]))lg.debug('returning: {0}'.format(wire['index']))return visiteddef find_neighbors(wire, devices, schematic):all_wires = devicesother_wires = [record for record in all_wires if record != wire]neighbors = []for other_wire in other_wires:if is_connected(wire, other_wire):neighbors.append(other_wire)return neighborsdef is_connected(wire_a, wire_b):if wire_a["RECORD"] == "27":a_line_segments = [(wire_a['coords'][i], wire_a['coords'][i + 1]) for i inrange(len(wire_a['coords']) - 1)]else:a_line_segments = [(wire_a['coords'][0], wire_a['coords'][0])]if wire_b["RECORD"] == "27":b_line_segments = [(wire_b['coords'][i], wire_b['coords'][i + 1]) for i inrange(len(wire_b['coords']) - 1)]else:b_line_segments = [(wire_b['coords'][0], wire_b['coords'][0])]# check if any vertices in wire_a lie on wire_bfor vertex in [vx for line in a_line_segments for vx in line]:for b_line in b_line_segments:b_xs = sorted(list(zip(*b_line))[0])b_ys = sorted(list(zip(*b_line))[1])if ((min(b_xs) <= vertex[0] <= max(b_xs))and (min(b_ys) <= vertex[1] <= max(b_ys))):return True# check if any vertices in wire_b lie on wire_afor vertex in [vx for line in b_line_segments for vx in line]:for a_line in a_line_segments:a_xs = sorted(list(zip(*a_line))[0])a_ys = sorted(list(zip(*a_line))[1])if ((min(a_xs) <= vertex[0] <= max(a_xs))and (min(a_ys) <= vertex[1] <= max(a_ys))):return True# check if both items are Power Ports with the same TEXT valueif ( wire_a["RECORD"] == "17" ) and ( wire_b["RECORD"] == "17" ) and ( wire_a["TEXT"] == wire_b["TEXT"] ):return Truereturn Falsedef main(args):schematic = parse(**vars(args))if args.output:json_file = open(output_folder, 'w', encoding='utf-8')json.dump(schematic, json_file, indent=4, ensure_ascii=False)else:print(schematic)if __name__ == "__main__":# 命令行使用方式# parser = argparse.ArgumentParser(description='转换.SchDoc文件转换为json', formatter_class=argparse.RawTextHelpFormatter)# parser.add_argument('input',#                     help='path/to/altiumschematic.schdoc 要分析的文件地址')# parser.add_argument('-o', '--output', dest='output',#                     help='path/to/jsonfile.json 输出json到的文件,否则打印到终端')# parser.add_argument('-f', '--format', dest='format', default='all_hierarchy',#                     choices=['all_list', 'all_hierarchy', 'parts_list', 'net_list'],#                     help=textwrap.dedent('''\#                     all-list: 展开列表中的所有记录#                     all-hierarchy: 在所有者和子结构中的所有记录#                     parts-list: 零件及其代号的列表#                     net-list: 零件引脚之间的网络列表,由其代号表示'''))# args = parser.parse_args()# main(args)# 直接调用函数使用方式SchDoc_path = "/home/hyh/data/Maintenance_test_data/AIN.SchDoc"format = "all_hierarchy"output_folder = os.path.join(os.path.dirname(SchDoc_path), os.path.basename(SchDoc_path).split(".")[0] + "_" + format + ".json")schematic = parse(SchDoc_path, format)json_file = open(output_folder, 'w', encoding='utf-8')json.dump(schematic, json_file, indent=4, ensure_ascii=False)

赞!赞!赞!

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

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

相关文章

apexcharts数据可视化之饼图

apexcharts数据可视化之饼图 有完整配套的Python后端代码。 本教程主要会介绍如下图形绘制方式&#xff1a; 基础饼图单色饼图图片饼图 基础饼图 import ApexChart from react-apexcharts;export function SimplePie() {// 数据序列const series [44, 55, 13, 43, 22]// …

APM2.8内置罗盘校准

如果你有外置罗盘&#xff0c;可以不用校准内置罗盘&#xff0c;可以忽略此文。推荐使用外置罗盘&#xff0c;内置罗盘容易受干扰。 使用内置罗盘需要插入飞控GPS接口旁边的跳线帽。如图&#xff1a; 如果要使用内置罗盘&#xff0c;而又加了GPS的&#xff0c;记得一定要把GPS…

【错误记录】HarmonyOS 运行报错 ( Failure INSTALL _PARSE _FAILED _USESDK _ERROR )

文章目录 一、报错信息二、问题分析三、解决方案 一、报错信息 在 DevEco Studio 中 , 使用 远程设备 , 向 P40 Failure[INSTALL_PARSE_FAILED_USESDK_ERROR] compileSdkVersion and releaseType of the app do not match the apiVersion and releaseType on the device. 二、…

SpringMVC枚举类型字段处理

在日常的项目开发中经常会遇到一些取值范围固定的字段&#xff0c;例如性别、证件类型、会员等级等&#xff0c;此时我们可以利用枚举来最大程度减少字段的乱定义&#xff0c;统一管理枚举的值。 SpringMVC中对于枚举也有默认的处理策略&#xff1a; 对于RequestParam&#xf…

静态测试---基于WorkList的活跃变量分析

本文主要用于记录在活跃变量分析实验中的报错及解决&#xff0c;涉及静态测试的详细原理内容较少&#xff0c;编译运行底层逻辑偏多。 一、实验要求 1&#xff09;使用llvm基于框架实现一个基于WorkList的活跃变量分析demo。变量在某个程序点有两种状态&#xff0c;live 或 dea…

利用 Scapy 库编写源路由攻击脚本

一、介绍 源路由攻击是一种网络攻击方法&#xff0c;攻击者通过利用IP数据包中的源路由选项来控制数据包的传输路径&#xff0c;从而绕过安全设备或防火墙&#xff0c;直接访问目标系统。源路由功能允许数据包的发送方指定数据包通过的路径&#xff0c;而不是由路由器根据路由…

Xshell 5(xmanager5)报错

总结 所有的错误都是因为Xshell版本太低&#xff0c;与新的Linux系统不兼容导致的。 所以解决办法都是使用Xshell7 XShell 7 &#xff08;解压、运行绿化.bat&#xff09; https://pan.baidu.com/s/151W_MeLrrceUZQIFiNlMdg?pwd8888错误1&#xff1a;找不到匹配的host key算…

【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)

【LeetCode刷题】Day 10 题目1&#xff1a;30. 串联所有单词的子串&#xff08;困难&#xff09;思路分析&#xff1a;思路1&#xff1a;滑动窗口哈希map 题目2&#xff1a;LCR 017.最小覆盖子串思路分析思路1&#xff1a;滑动窗口哈希表 题目1&#xff1a;30. 串联所有单词的子…

基于51单片机的直流电机调速设计

一.硬件方案 本系统采用STC89C51控制输出数据&#xff0c;由单片机IO口产生PWM信号&#xff0c;送到直流电机&#xff0c;直流电机通过测速电路将实时转速送回单片机&#xff0c;进行转速显示&#xff0c;从而实现对电机速度和转向的控制&#xff0c;达到直流电机调速的目的。…

qt把虚拟键盘部署到arm开发板上(imx6ull)

分为了qt官方配置的虚拟键盘以及各路大神自己开源的第三方键盘&#xff0c;我本来想尝试利用官方键盘结果一直失败&#xff0c;最后放弃了&#xff0c;后面我用的第三方键盘参考了如下文章&#xff1a; https://blog.csdn.net/2301_76250105/article/details/136441243 https…

git 学习随笔

git 学习随笔 基本概念 git 对待数据类似快照流的形式而不是类似 cvs 那样的纪录文件随时间逐步积累的差异 git 中所有数据在存储钱都会计算校验和&#xff08;hash) 三种状态&#xff1a;已提交(committed)&#xff0c;已修改(modified)&#xff0c;已暂存(staged)。 add…

二叉树习题精讲-单值二叉树

单值二叉树 965. 单值二叉树 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/univalued-binary-tree/description/ 判断这里面的所有数值是不是一样 方案1&#xff1a;遍历 方案2&#xff1a;拆分子问题 /*** Definition for a binary tree node.* struc…

k8s群集调度之 pod亲和 node亲和 标签指定

目录 一 调度约束 1.1K8S的 List-Watch 机制 ⭐⭐⭐⭐⭐ 1.1.1Pod 启动典型创建过程 二、调度过程 2.1Predicate&#xff08;预选策略&#xff09; 常见的算法 2.2priorities&#xff08;优选策略&#xff09;常见的算法 三、k8s将pod调度到指定node的方法 3.1指…

最强端侧多模态模型MiniCPM-V 2.5,8B 参数,性能超越 GPT-4V 和 Gemini Pro

前言 近年来&#xff0c;人工智能领域掀起了一股大模型热潮&#xff0c;然而大模型的巨大参数量级和高昂的算力需求&#xff0c;限制了其在端侧设备上的应用。为了打破这一局限&#xff0c;面壁智能推出了 MiniCPM 模型家族&#xff0c;致力于打造高性能、低参数量的端侧模型。…

【深度学习】xformers与pytorch的版本对应关系

https://github.com/facebookresearch/xformers/tree/v0.0.23 找tag&#xff1a; tag下面写了对应关系&#xff1a; 安装指令就是&#xff1a; pip install xformers0.0.23 --no-deps -i https://download.pytorch.org/whl/cu118

react ant 表格实现 拖拽排序和多选

项目背景 : react ant 要实现 : 有多选功能(实现批量删除 , 也可以全选) 可以拖拽(可以复制 , 方便顶部的搜索功能) 要实现效果如下 1 这是最初的拖拽功能实现 , 不能复制表格里的内容 , 不符合要求 2 更改了ROW的内容 , 实现了可以复制表格内容 代码 //控制是否可以选中表格…

基于粒子群算法的网络最优节点部署优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于粒子群算法的网络最优节点部署优化,实现WSN网络的节点覆盖最大化。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 .................…

Golang | Leetcode Golang题解之第118题杨辉三角

题目&#xff1a; 题解&#xff1a; func generate(numRows int) [][]int {ans : make([][]int, numRows)for i : range ans {ans[i] make([]int, i1)ans[i][0] 1ans[i][i] 1for j : 1; j < i; j {ans[i][j] ans[i-1][j] ans[i-1][j-1]}}return ans }

Notes for video: EDC-Con 2022/01 - EDC Conceptual Overview and Architecture

Eclipse Dataspace Connector 中文概念 Eclipse Dataspace Connector (EDC) 是一个开源项目&#xff0c;旨在提供一种标准化的方法来连接和共享数据空间中的数据。它是 Eclipse Foundation 下的一个项目&#xff0c;目标是促进数据共享和数据交换的互操作性。以下是 EDC 的一些…

【STL库源码剖析】list 简单实现

从此音尘各悄然 春山如黛草如烟 目录 list 的结点设计 list 的迭代器 list 的部分框架 迭代器的实现 容量相关相关函数 实现 insert 在指定位置插入 val 实现 push_back 在尾部进行插入 实现 erase 在指定位置删除 实现 pop_back 在尾部进行删除 实现 list 的头插、头删 实现…