在Windows Python开发中,配置文件管理是每个开发者都会遇到的问题。是否还在为硬编码的配置信息而苦恼?是否想要一个既灵活又易于维护的配置方案?
JSON配置文件凭借其轻量级、可读性强、跨平台兼容的特点,已成为现代Python应用的首选配置格式。无论是桌面应用、Web服务还是上位机开发,掌握JSON配置文件的读写技巧都是必备技能。
本文将从实际开发场景出发,详细讲解Python读写JSON配置文件的各种方法和最佳实践,帮助你构建更加专业和可维护的应用程序。
在Python开发中,常见的配置文件格式有:
一个良好的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"]
}
}
在当今企业数字化转型的浪潮中,单点登录(SSO)已成为提升用户体验和系统安全性的关键技术。OpenID Connect(OIDC)作为现代身份认证标准,被越来越多的企业采用。本文将通过一个完整的Python实战项目,带你深入理解OIDC协议,并手把手教你构建一个功能完善的企业级OIDC客户端。
无论你是刚接触身份认证的Python开发者,还是希望深入理解OIDC实现细节的技术专家,这篇文章都将为你提供实用的代码实战和最佳实践指导。我们将从零开始构建一个支持完整认证流程、JWT验证、PKCE安全增强的OIDC客户端。

在企业环境中实现OIDC客户端,开发者通常面临以下几个关键挑战:
1. 协议复杂性
2. 安全性要求
3. 用户体验
在工业自动化和数据采集领域,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}");
}
}
}
}
OPC(OLE for Process Control)数据访问(DA)是工业自动化中常用的通信协议,用于在不同软件和硬件系统之间交换实时数据。本文将详细介绍如何使用C#和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);
}
}
OPC(Open Platform Communications)是工业自动化领域标准的通信协议,其中OPC DA(Data Access)是最常用的通信标准之一。本文将深入探讨如何使用OpcClientSdk在C#中开发OPC DA客户端应用。
OPC DA是一种标准化的工业通信接口,允许不同设备和软件系统之间进行实时数据交换。它主要用于:
OpcClientSdk和OpcClientSdk.DaC#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();
}
}
}
