编辑
2025-12-01
Python
00

在Windows Python开发中,配置文件管理是每个开发者都会遇到的问题。是否还在为硬编码的配置信息而苦恼?是否想要一个既灵活又易于维护的配置方案?

JSON配置文件凭借其轻量级、可读性强、跨平台兼容的特点,已成为现代Python应用的首选配置格式。无论是桌面应用、Web服务还是上位机开发,掌握JSON配置文件的读写技巧都是必备技能。

本文将从实际开发场景出发,详细讲解Python读写JSON配置文件的各种方法和最佳实践,帮助你构建更加专业和可维护的应用程序。

🔍 问题分析

为什么选择JSON作为配置文件格式?

在Python开发中,常见的配置文件格式有:

  • INI文件:适合简单配置,但不支持复杂数据结构
  • XML文件:功能强大但语法冗长
  • YAML文件:可读性好但需要额外依赖
  • JSON文件:原生支持,结构清晰,是最佳选择

JSON配置文件的核心优势

  1. 原生支持:Python内置json模块,无需额外安装
  2. 数据类型丰富:支持字符串、数字、布尔值、列表、字典等
  3. 跨平台兼容:在Windows、Linux、macOS下都能正常工作
  4. 易于维护:结构清晰,便于版本控制和团队协作

💡 解决方案

🏗️ 配置文件结构设计

一个良好的JSON配置文件应该具备以下特征:

JSON
{ "app_info": { "name": "MyPythonApp", "version": "1.0.0", "author": "Developer" }, "database": { "host": "localhost", "port": 3306, "username": "root", "password": "password123", "database_name": "myapp_db" }, "logging": { "level": "INFO", "file_path": "logs/app.log", "max_size": "10MB", "backup_count": 5 }, "features": { "auto_save": true, "theme": "dark", "language": "zh-CN", "plugins": ["plugin1", "plugin2"] } }
编辑
2025-12-01
Python
00

在当今企业数字化转型的浪潮中,单点登录(SSO)已成为提升用户体验和系统安全性的关键技术。OpenID Connect(OIDC)作为现代身份认证标准,被越来越多的企业采用。本文将通过一个完整的Python实战项目,带你深入理解OIDC协议,并手把手教你构建一个功能完善的企业级OIDC客户端。

无论你是刚接触身份认证的Python开发者,还是希望深入理解OIDC实现细节的技术专家,这篇文章都将为你提供实用的代码实战和最佳实践指导。我们将从零开始构建一个支持完整认证流程、JWT验证、PKCE安全增强的OIDC客户端。

🎯 问题分析:企业级OIDC客户端的挑战

image.png

核心挑战识别

在企业环境中实现OIDC客户端,开发者通常面临以下几个关键挑战:

1. 协议复杂性

  • OIDC建立在OAuth 2.0之上,涉及多个端点和复杂的token交换流程
  • 需要处理授权码流程、隐式流程、客户端凭证流程等多种认证模式
  • JWT token验证涉及复杂的密码学操作

2. 安全性要求

  • 必须实现PKCE(Proof Key for Code Exchange)防止授权码拦截攻击
  • JWT签名验证确保token完整性
  • 安全的本地回调处理机制

3. 用户体验

  • 自动浏览器集成和回调处理
  • 友好的错误提示和状态反馈
  • 支持不同网络环境的灵活配置
编辑
2025-11-30
C#
00

项目背景

在工业自动化和数据采集领域,OPC(OLE for Process Control)是一种标准通信协议,用于在不同工业设备和软件系统之间实现数据交换。OPC DA(Data Access)是最常用的OPC协议之一,用于读取和写入实时数据。

控制台应用架构

项目依赖

核心类设计

C#
using OpcClientSdk.Da; using OpcClientSdk; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppDaBrowser { /// <summary> /// OPC DA服务器浏览器 /// </summary> public class OpcDaBrowser { /// <summary> /// 日志记录委托 /// </summary> private Action<string> Logger { get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="logger">日志记录方法</param> public OpcDaBrowser(Action<string> logger = null) { Logger = logger ?? Console.WriteLine; } /// <summary> /// 发现OPC服务器 /// </summary> /// <returns>可用服务器列表</returns> public List<OpcServer> DiscoverServers() { try { Logger("开始发现OPC服务器..."); // 发现OPC DA 2.0服务器 List<OpcServer> servers = OpcDiscovery.GetServers(OpcSpecification.OPC_DA_20); Logger($"发现 {servers.Count} 个OPC服务器"); // 打印服务器详情 foreach (var server in servers) { Logger($"服务器名称: {server.ServerName}"); Logger($"服务器地址: {server.Url}"); } return servers; } catch (Exception ex) { Logger($"服务器发现失败: {ex.Message}"); return new List<OpcServer>(); } } /// <summary> /// 连接到OPC服务器 /// </summary> /// <param name="serverUrl">服务器URL</param> /// <returns>连接的服务器实例</returns> public TsCDaServer ConnectToServer(string serverUrl) { try { TsCDaServer server = new TsCDaServer(); Logger($"正在连接服务器: {serverUrl}"); // 连接服务器 server.Connect(serverUrl); Logger("成功连接到服务器"); return server; } catch (Exception ex) { Logger($"服务器连接失败: {ex.Message}"); return null; } } /// <summary> /// 浏览服务器地址空间 /// </summary> /// <param name="server">OPC服务器实例</param> public void BrowseAddressSpace(TsCDaServer server) { if (server == null) return; try { Logger("开始浏览地址空间..."); // 配置浏览过滤器 TsCDaBrowseFilters filters = new TsCDaBrowseFilters { BrowseFilter = TsCDaBrowseFilter.All, ReturnAllProperties = true, ReturnPropertyValues = true }; // 初始浏览位置 TsCDaBrowsePosition position; // 浏览根节点 TsCDaBrowseElement[] elements = server.Browse( new OpcItem(""), // 根路径 filters, out position ); // 递归打印浏览元素 PrintBrowseElements(elements, 0); } catch (Exception ex) { Logger($"地址空间浏览失败: {ex.Message}"); } } /// <summary> /// 递归打印浏览元素 /// </summary> /// <param name="elements">浏览元素数组</param> /// <param name="depth">树深度</param> private void PrintBrowseElements(TsCDaBrowseElement[] elements, int depth) { if (elements == null) return; foreach (var element in elements) { string indent = new string(' ', depth * 2); Logger($"{indent}- 名称: {element.Name}"); Logger($"{indent} 是否为项目: {element.IsItem}"); // 如果是项目,获取详细信息 if (element.IsItem) { Logger($"{indent} 项目路径: {element.ItemPath}"); Logger($"{indent} 项目名称: {element.ItemName}"); } } } /// <summary> /// 获取项目属性 /// </summary> /// <param name="server">OPC服务器实例</param> /// <param name="itemPath">项目路径</param> public void GetItemProperties(TsCDaServer server, string itemPath) { if (server == null) return; try { Logger($"获取项目 {itemPath} 的属性"); // 创建OPC项目 OpcItem[] items = new[] { new OpcItem(itemPath) }; // 获取属性 TsCDaItemPropertyCollection[] properties = server.GetProperties( items, null, // 所有属性 true // 返回属性值 ); // 打印属性 foreach (var propertyCollection in properties) { Logger($"项目 {propertyCollection.ItemPath} 的属性:"); foreach (var property in propertyCollection) { dynamic d = property; if (d.Result.Succeeded()) { Logger($" 属性: {d.Description}"); Logger($" 值: {OpcConvert.ToString(d.Value)}"); } } } } catch (Exception ex) { Logger($"获取项目属性失败: {ex.Message}"); } } } }
编辑
2025-11-30
C#
00

引言

OPC(OLE for Process Control)数据访问(DA)是工业自动化中常用的通信协议,用于在不同软件和硬件系统之间交换实时数据。本文将详细介绍如何使用C#和OpcClientSdk实现OPC DA的异步读写操作。

开发环境准备

在开始之前,需要具备以下环境:

  • Visual Studio
  • .NET Framework
  • OpcClientSdk类库
  • OPC DA服务器

关键代码解析

服务器发现与连接

C#
// 发现可用的OPC DA服务器 private void Form1_Load(object sender, EventArgs e) { List<OpcServer> servers = OpcDiscovery.GetServers(OpcSpecification.OPC_DA_20); if (servers != null && servers.Count > 0) { foreach (OpcServer server in servers) { // 将服务器名称添加到下拉列表 cboServer.Items.Add(server.ServerName); } } } // 连接到选定的OPC服务器 private void btnConnect_Click(object sender, EventArgs e) { try { // 设置等待光标 Cursor = Cursors.WaitCursor; // 创建OPC服务器连接URL OpcUrl opcUrl = new OpcUrl(OpcSpecification.OPC_DA_20, OpcUrlScheme.DA, cboServer.Text); // 连接到OPC服务器 _OpcDaServer.Connect(opcUrl, null); } catch (OpcResultException exe) { MessageBox.Show(exe.Message, "连接错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }
编辑
2025-11-30
C#
00

OPC(Open Platform Communications)是工业自动化领域标准的通信协议,其中OPC DA(Data Access)是最常用的通信标准之一。本文将深入探讨如何使用OpcClientSdk在C#中开发OPC DA客户端应用。

OPC DA基本概念

OPC DA是什么?

OPC DA是一种标准化的工业通信接口,允许不同设备和软件系统之间进行实时数据交换。它主要用于:

  • 从工业设备读取实时数据
  • 向设备写入控制数据
  • 监控数据变化

关键组件

  • Server(服务器):提供数据访问的工业设备或软件
  • Client(客户端):请求和处理数据的应用程序
  • Item(项目):可以读写的具体数据点

OpcClientSdk开发实践

开发环境准备

  • 安装OpcClientSdk
  • 添加必要的引用:OpcClientSdkOpcClientSdk.Da

代码详解

连接OPC服务器

C#
const string serverUrl = "opcda://localhost/Kepware.KEPServerEX.V6"; TsCDaServer myDaServer = new TsCDaServer(); myDaServer.Connect(serverUrl);

创建订阅组

C#
TsCDaSubscriptionState groupState = new TsCDaSubscriptionState { Name = "MyGroup" }; TsCDaSubscription group = (TsCDaSubscription)myDaServer.CreateSubscription(groupState);

定义和添加监控项目

C#
TsCDaItem[] items = new TsCDaItem[1]; items[0] = new TsCDaItem { ItemName = "Channel.Device.L1", ClientHandle = 100, Active = true };

基础例子

C#
using OpcClientSdk.Da; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppOpcDa { internal class Program { static void Main(string[] args) { const string serverUrl = "opcda://localhost/Kepware.KEPServerEX.V6"; TsCDaServer myDaServer = new TsCDaServer(); myDaServer.Connect(serverUrl); if (myDaServer.IsConnected) { Console.WriteLine("Connected to OPC DA Server"); TsCDaSubscriptionState groupState = new TsCDaSubscriptionState { Name = "MyGroup" }; TsCDaSubscription group = (TsCDaSubscription)myDaServer.CreateSubscription(groupState); TsCDaItem[] items = new TsCDaItem[1]; TsCDaItemResult[] itemResults; items[0] = new TsCDaItem(); items[0].ItemName = "Channel.Device.L1"; // 指定要读取的 OPC 标签名称 items[0].ClientHandle = 100; // 客户端自定义句柄,用于标识这个数据项 items[0].MaxAgeSpecified = true; // 启用 MaxAge 设置 items[0].MaxAge = 0; // 从服务器直接读取实时数据 items[0].Active = true; // 设置数据项为活动状态 items[0].ActiveSpecified = true; // 启用 Active 设置 // 读取数据 TsCDaItemValueResult[] itemValues = myDaServer.Read(items); // 遍历并输出读取到的数据 for (int i = 0; i < itemValues.Length; i++) { // 检查是否成功读取 if (!itemValues[i].Result.IsError()) { Console.WriteLine($"Item: {items[i].ItemName}"); Console.WriteLine($"Value: {itemValues[i].Value}"); Console.WriteLine($"Quality: {itemValues[i].Quality}"); Console.WriteLine($"Timestamp: {itemValues[i].Timestamp}"); } else { Console.WriteLine($"Error reading item {items[i].ItemName}: {itemValues[i].Result}"); } } itemResults = group.AddItems(items); for (int i = 0; i < itemResults.GetLength(0); i++) { if (itemResults[i].Result.IsError()) { Console.WriteLine(String.Format(" Item {0} could not be added to the group", itemResults[i].ItemName)); } } } Console.ReadKey(); } } }

image.png