【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]// …

AI大模型:掌握未知,开启未来

AI大模型的工作原理 AI大模型是指通过大量数据和复杂算法训练出的能够理解和生成自然语言文本的人工智能模型。它们背后的核心技术主要包括深度学习、神经网络和自然语言处理。以下是详细的工作原理以及通俗易懂的类比&#xff1a; 1. 数据收集和预处理 AI大模型的训练首先需…

【面试】什么是Java堆内存溢出?

目录 1. 概念2. 堆内存溢出的原因2.1 内存泄露2.2 堆内存设置过小2.3 大量对象创建2.4 静态集合类2.5 外部资源没有及时释放 3. 避免内存溢出的建议 1. 概念 1.Java堆内存溢出&#xff08;Java Heap Memory Overflow&#xff09;。2.是指Java虚拟机&#xff08;JVM&#xff09…

java —— 常用类

一、System 类 System 类内部的构造方法是 private 修饰的&#xff0c;所以不能实例化&#xff0c;普通方法均为静态方法。 &#xff08;一&#xff09;.currentTimeMillis() 括号内无参数&#xff0c;返回值为距离1970年1月1日0时0分0秒之间的毫秒数。 long timeSystem.cu…

全面解析开源RTSP流媒体服务器:功能、性能与应用场景对比

本文综合分析了多个开源RTSP流媒体服务器&#xff0c;包括EasyDarwin、RtspServer、SRS等&#xff0c;深入探讨它们的功能特性、技术实现、性能对比及应用场景&#xff0c;旨在为开发者提供全面的选型参考。 文章目录 开源RTSP流媒体服务器概述RTSP协议简介开源RTSP服务器的重要…

【JavaScript】P5 数组与常量

目录 1 数组1.1 数组的声明1.2 数组的基本术语 2 常量 1 数组 如果将多个数据存储在单一个变量名下 -> 数组。 数组&#xff08;array&#xff09;&#xff0c;可以将一组数据存储在单个变量名下。 1.1 数组的声明 let 数组名 [数据1, 数据2, ..., 数据n]数组是有序的&a…

APM2.8内置罗盘校准

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

深入探索Qt框架系列之元对象编译器

上一篇文章简单介绍了Qt框架中的三大编译器&#xff08;MOC、UIC、RCC&#xff09;&#xff0c;其中我认为最核心&#xff0c;最重要的就是元对象编译器&#xff08;MOC&#xff09;&#xff0c;下面我们将深入探索MOC生成的代码&#xff0c;并逐步解析。 本文将以下面的源码来…

【错误记录】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…

管理开发进度

在系统开发现场&#xff0c;必须要对项目的推进状况进行管理。不过&#xff0c;针对大型项目&#xff0c;要一下子对其整体进行统一的管理是很困难的。这时就需要将其划分成更小的单位进行管理。 这种用于分解的单位被称为任务。以任务为单位对开发进度进行管理的方法…

平常心看待已发生的事

本篇主要记录自己在阅读此篇文章&#xff08;文章链接&#xff1a; 这才是扼杀员工积极性的真正原因&#xff08;管理者必读&#xff09; &#xff09;和这两天京东的东哥“凡是长期业绩不好&#xff0c;从来不拼搏的人&#xff0c;不是我的兄弟”观点后的一些想法。 自己在微…

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

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

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

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

计数排序法

计数排序的核心在于将输入数据转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序&#xff0c;计数排序要求输入的数据必须是有确定范围的整数。 当输入的元素是n个0到k之间的整数时&#xff0c;它的运行时间复杂度是O(nk)。计数排序不是比较排序&#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;达到直流电机调速的目的。…

20240528训练题目(2022 国际大学生程序设计竞赛亚洲区域赛 (南京站))

D题 题目描述 You’re the researcher of the International Chat Program Company (ICPC). Today, you discover the following chat history when reviewing some research data. SUA (2022/12/04 23:01:25) I’m out of ideas for competitive programming problems! Pl…

【TC8】如何测试IOP中PHY芯片的Llink-up time

在TC8一致性测试用例中,物理层的测试用例分为两个部分:IOP和PMA。其中IOP中对PHY芯片的Link-up时间的测试,又包含三个测试用例。它们分别是: OABR_LINKUP_01: Link-up time - Trigger: Power on Link PartnerOABR_LINKUP_02: Link-up time - Trigger: Power on DUTOABR_LIN…