巧用python之--模仿PLC(PLC模拟器)

工作中用到了VM(VisionMaster4.3)有时候需要和PLC打交道,但是PLC毕竟是别人的,不方便修改别人的程序,这时候需要一个灵活的PLC模拟器是多么好呀!

先说背景:

PLC型号
汇川Easy521:
Modbus TCP 192.168.1.10:502 
在汇川Easy521中Modbus保持寄存器=D寄存器 ,在modbus协议中 0-4区 3区就是 保持寄存器(R/W)

那么事情很简单了:只需要做一个ModbusTCP的服务端 就能模拟PLC:

Modbus RTU是主从结构 分 Modbus  主机 / 从机  Master / Slave;

RTU模式主从 主机会轮询从机 问一次答一次;   一般电脑做主机 PLC做从机;

但是当电脑同时连接很多台PLC,电脑做主机,主动询问PLC那么电脑的压力会很大;

这时候电脑可以做从机,多个PLC做主机,电脑端成了被动,那么电脑压力会小很多;

(适用于MODBUS RTU &TCP) 扯远了;

好了这里只说Modbus_TCP 粗略的说Modbus_TCP的报文实际就是RTU 增加文件头去掉CRC校验;

注意下面说的Tcp都指得是modbusTCP;;;

客户端 服务器 Client/Server

VisionMaster4.3只支持Modbus> TcpClient 

TcpClient按主从结构分是主机/(Master), PLC扮演的是 Modbus> TcpServer (Slave)

所以在Modbustcp这里,服务器是modbus从机,客户端是modbus主机;

由 TcpClient(VM) 去 轮询 TcpServer(PLC);

  好了,搞清楚原理了,下来就是,模拟一个TcpServer(PLC);就是一个可以用的PLC模拟器.

当然你用Modsim/ModScan也可以,但是操作不便;

;因为之前就做过一些用hsl库C#,模拟modbusTCPServer,这里程序放出来吧;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Remoting.Contexts;
using System.Threading;using HslCommunication;namespace MB_TCPServer
{public partial class Form1 : Form{HslCommunication.ModBus.ModbusTcpServer modbusTcpServer;public Form1(){InitializeComponent();modbusTcpServer  = new HslCommunication.ModBus.ModbusTcpServer();modbusTcpServer.ServerStart(502);modbusTcpServer.Write("50", (UInt16)1);modbusTcpServer.Write("60", (UInt16)1);modbusTcpServer.Write("61", (UInt16)1);OperateResult <UInt16> intReg_100 = modbusTcpServer.ReadUInt16("50");                    // 读取输入寄存器100的值if (intReg_100.IsSuccess){Console.WriteLine("success!");Console.WriteLine("");}else{Console.WriteLine("failed:" + intReg_100.Message);}List<string> ipAddresses = GetIpAddresses();//调用Console.WriteLine(ipAddresses.Count);//有几个ipforeach (string ipAddress in ipAddresses){Console.WriteLine(ipAddress);//ip分别有哪些comboBox_me_ip.Items.Add(ipAddress);}comboBox_me_ip.SelectedItem   = 1; }private void button1_Click(object sender, EventArgs e){}public static List<string> GetIpAddresses(){List<string> ipAddresses = new List<string>();try{// 获取本地主机名string hostName = Dns.GetHostName();// 使用主机名获取IP地址信息IPHostEntry hostEntry = Dns.GetHostEntry(hostName);// 获取IP地址列表foreach (IPAddress ipAddress in hostEntry.AddressList){// 确保IP地址不是IPv6的环回地址或者IPv4的环回地址if (!ipAddress.IsIPv4MappedToIPv6 && ipAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6){ipAddresses.Add(ipAddress.ToString());}}}catch (Exception ex){}return ipAddresses;/*List<string> ipAddresses = GetIpAddresses();//调用Console.WriteLine(ipAddresses.Count);//有几个ipforeach (string ipAddress in ipAddresses){Console.WriteLine(ipAddress);//ip分别有哪些}*/}private void button_trig_Click(object sender, EventArgs e){modbusTcpServer.Write("50", (UInt16)10);    OperateResult<UInt16> intReg_100 = modbusTcpServer.ReadUInt16("50");                    // 读取输入寄存器100的值if (intReg_100.IsSuccess){Console.WriteLine("success!");Console.WriteLine(intReg_100.Content);textBox_trig.Text= intReg_100.Content.ToString();}else{Console.WriteLine("failed:" + intReg_100.Message);}}private void button2_Click(object sender, EventArgs e){modbusTcpServer.Write("50", (UInt16)0);OperateResult<UInt16> intReg_100 = modbusTcpServer.ReadUInt16("50");                    // 读取输入寄存器100的值if (intReg_100.IsSuccess){Console.WriteLine("success!");Console.WriteLine(intReg_100.Content);textBox_trig.Text = intReg_100.Content.ToString();}else{Console.WriteLine("failed:" + intReg_100.Message);}}private void button3_Click(object sender, EventArgs e){button_trig_Click(null,null);Thread.Sleep(200);// 太快vm反应不过来button2_Click(null, null);}}
}

 可以看出实际有用的只有这些:其余都是 winform界面;

using HslCommunication;
namespace MB_TCPServer
{public partial class Form1 : Form{HslCommunication.ModBus.ModbusTcpServer modbusTcpServer;public Form1(){modbusTcpServer  = new HslCommunication.ModBus.ModbusTcpServer();modbusTcpServer.ServerStart(502);modbusTcpServer.Write("50", (UInt16)1);modbusTcpServer.Write("60", (UInt16)1);modbusTcpServer.Write("61", (UInt16)1);OperateResult <UInt16> intReg_100 = modbusTcpServer.ReadUInt16("50");                    if (intReg_100.IsSuccess){Console.WriteLine("success!");Console.WriteLine("");}else{Console.WriteLine("failed:" + intReg_100.Message);}}}
}

要求很简单就是要一个 界面带按钮可以修改 模拟TcpServer(PLC);内指定寄存器的数值 读取指定数值即可.

但是那天出门只带了一个平板笔记本,且没转VS虽然有程序但无法编译,所以python登场.

.虽然python有modbus库 minimalmodbus pymodbus ;等但是没有hsl好用 因为之前用hsl ;

直接用pythonnet在python里面调用 .net 版的hsl,实际python也有hsl但是需要授权,C# 版7.001以及以下版本是免费的,对于干这件事情是绰绰有余的,这里对hsl作者表示感谢;;;

于是就有了如下 产物: 有界面 有按钮 有结果显示 有数字显示 可以模拟 PLC的程序 且一共200行;

当然必须安装pythonnet ,且依赖的外部 .net  dll文件要和.py程序在一个目录 

import os,sys,time
import tkinter as tk
from tkinter import messagebox#import win32api,win32con,win32guifrom ctypes import *
#需要安装 pywin32
def cmd(s="pause"):os.system(s)
def p(s):print(s);return s
win = tk.Tk()
win.title("(汇川Easy521): PLC模拟器")
win.geometry('960x280')
win.config(background ="#00aa00")
winscrwidth=win.winfo_screenwidth()# 基础库
import os,sys,time
from ctypes import *
def cmd(s="pause"):os.system(s)
#C语言那一套 拿过来 C#那一套数据类型拿过来
import clr,System
from System import String, Char, Int32,UInt16, Int64, Environment, IntPtr#导包
print(clr.AddReference("HslCommunication"))
#现在可以当python自己的库来用了
import HslCommunicationmodbusTcpServer  = HslCommunication.ModBus.ModbusTcpServer();
modbusTcpServer.ServerStart(502);#必须指定泛型 否则无效 UInt16(65535)
#初始化寄存器 指定3区 设定初始值
modbusTcpServer.Write("x=3;100", UInt16(0));# 触发 1 2 3 4对应4流道
modbusTcpServer.Write("x=3;101", UInt16(0));# 料号 0 1 2
modbusTcpServer.Write("x=3;105", UInt16(0));# 结果 11(OK) 12(NG) 13(ERROR)
modbusTcpServer.Write("x=3;106", UInt16(0));# 心跳0/1# 获取时间的函数
def gettime():# 获取当前时间dstr.set(f"""{time.strftime("%H:%M:%S")}  >>127.0.0.1:502""")try: #必须try 否则要在界面控件创建完成后销毁前调用 图省事try完事intReg_100 = modbusTcpServer.ReadUInt16("100");#返回的是 Oper类型 不是int数值 intReg_101 = modbusTcpServer.ReadUInt16("101");intReg_105 = modbusTcpServer.ReadUInt16("105");intReg_106 = modbusTcpServer.ReadUInt16("106");entry1.delete(0, "end");entry1.insert(0,f'{intReg_100.Content}')#entry2.delete(0, "end");entry2.insert(0,f'{intReg_101.Content}')#entry3.delete(0, "end");entry3.insert(0,f'{intReg_105.Content}')#entry4.delete(0, "end");entry4.insert(0,f'{intReg_106.Content}')#    Reg=intReg_105.Contentif(Reg==0):rrr.config(text='None'); rrr.config(bg='#00aa00')if(Reg==11):rrr.config(text='ok');   rrr.config(bg='#00ff00')if(Reg==12):rrr.config(text='ng');   rrr.config(bg='#ff0000')if(Reg==13):rrr.config(text='Error');rrr.config(bg='#ffff00')##except:pass# 每隔 1s 调用一次 gettime()函数来获取时间win.after(200, gettime)
# 生成动态字符串
dstr = tk.StringVar()
# 利用 textvariable 来实现文本变化
lb = tk.Label(win,textvariable=dstr,fg='green',font=("微软雅黑",18))
lb.pack()
gettime()# 调用生成时间的函数tk.Label(win,text='触发(D100[1]):',fg='black',font=("微软雅黑",15)).place (x=0,y=40, width=150, height=30)
entry1 = tk.Entry(win)# 创建输入框控件
entry1.place (x=300,y=40, width=60, height=30)#relx=0.01,relheight=0.4
#.pack(padx=20, pady=20)# 放置输入框,并设置位置
entry1.delete(0, "end")
entry1.insert(0,'0')# 插入默认文本
print(entry1.get())# 得到输入框字符串
# entry1.delete(0, tk.END)# 删除所有字符#-----------------------------------------------------------------------------
def button_click_100():#按钮modbusTcpServer.Write("x=3;105", UInt16(0));#结果清零modbusTcpServer.Write("x=3;100", UInt16(1));pass
button_100 = tk.Button(win,text="触发写1",command=button_click_100)
button_100.place (x=380,y=40, width=60, height=30)def button_click_100_2():modbusTcpServer.Write("x=3;105", UInt16(0));#结果清零modbusTcpServer.Write("x=3;100", UInt16(2));pass
button_100 = tk.Button(win,text="触发写2",command=button_click_100_2)
button_100.place (x=380+80,y=40, width=60, height=30)def button_click_100_3():modbusTcpServer.Write("x=3;105", UInt16(0));#结果清零modbusTcpServer.Write("x=3;100", UInt16(3));pass
button_100 = tk.Button(win,text="触发写3",command=button_click_100_3)
button_100.place (x=380+80+80,y=40, width=60, height=30)def button_click_100_4():modbusTcpServer.Write("x=3;105", UInt16(0));#结果清零modbusTcpServer.Write("x=3;100", UInt16(4));pass
button_100 = tk.Button(win,text="触发写4",command=button_click_100_4)
button_100.place (x=380+80+80+80,y=40, width=60, height=30)def button_click_101():#按钮 触发写0modbusTcpServer.Write("x=3;100", UInt16(0));pass
button_100 = tk.Button(win,text="触发写0",command=button_click_101)
button_100.place (x=380+80+80+80+80,y=40, width=60, height=30)def button_click_102():#按钮 触发写0modbusTcpServer.Write("x=3;105", UInt16(0));#结果清零modbusTcpServer.Write("x=3;100", UInt16(1));time.sleep(0.2)modbusTcpServer.Write("x=3;100", UInt16(0));pass
button_100 = tk.Button(win,text="触发写1(延时200)写0",command=button_click_102)
button_100.place (x=380+80+80+80+80+80,y=40, width=160, height=30)
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
def button_click_200():#按钮modbusTcpServer.Write("x=3;101", UInt16(0));pass
button_100 = tk.Button(win,text="料号写0",command=button_click_200)
button_100.place (x=380,y=40+40, width=60, height=30)def button_click_201():#按钮 触发写0modbusTcpServer.Write("x=3;101", UInt16(1));pass
button_100 = tk.Button(win,text="料号写1",command=button_click_201)
button_100.place (x=380+80,y=40+40, width=60, height=30)def button_click_202():#按钮 触发写0modbusTcpServer.Write("x=3;101", UInt16(2));pass
button_100 = tk.Button(win,text="料号写2",command=button_click_202)
button_100.place (x=380+80+80,y=40+40, width=160, height=30)
#-----------------------------------------------------------------------------tk.Label(win,text='料号(D101[0/1/2]):',fg='black',font=("微软雅黑",15)).place (x=0,y=40+40, width=180, height=30)
entry2 = tk.Entry(win)# 创建输入框控件
entry2.place (x=300,y=40+40, width=60, height=30)#relx=0.01,relheight=0.4
#.pack(padx=20, pady=20)# 放置输入框,并设置位置
entry2.delete(0, "end")# 插入默认文本
entry2.insert(0,'0')
print(entry2.get())# 得到输入框字符串
# entry1.delete(0, tk.END)# 删除所有字符
#relx、rely relheight、relwidth anchor=NEtk.Label(win,text='结果:(D105[11(OK)/12(NG)]):',fg='black',font=("微软雅黑",15)).place (x=0,y=40+40+40, width=280, height=30)entry3 = tk.Entry(win)# 创建输入框控件
entry3.place (x=300,y=40+40+40, width=60, height=30)#relx=0.01,relheight=0.4
#.pack(padx=20, pady=20)# 放置输入框,并设置位置
entry3.delete(0, "end")# 插入默认文本
entry3.insert(0,'0')
print(entry3.get())# 得到输入框字符串rrr=tk.Label(win,text='ok',fg='black',font=("微软雅黑",15))
rrr.place (x=400,y=40+40+40, width=150, height=30)
#rrr.config(text='ok');rrr.config(bg='#00ff00')
#rrr.config(text='ng');rrr.config(bg='#ff0000')
rrr.config(text='None');rrr.config(bg='#00aa00')tk.Label(win,text='心跳:(D106[0/1]):',fg='black',font=("微软雅黑",15)).place (x=0,y=40+40+40+40, width=180, height=30)entry4 = tk.Entry(win)# 创建输入框控件
entry4.place (x=300,y=40+40+40+40, width=60, height=30)#relx=0.01,relheight=0.4
#.pack(padx=20, pady=20)# 放置输入框,并设置位置
entry4.delete(0, "end")# 插入默认文本
entry4.insert(0,'0')
print(entry4.get())# 得到输入框字符串def QueryWindow():if messagebox.showwarning("窗口关闭?"):win.destroy()
win.protocol('WM_DELETE_WINDOW', QueryWindow)
win.mainloop()

这样VM 写电脑IP 或者 127.0.0.1:502 就可以把这个程序当成PLC连接了.

在手上没有PLC的情况下很方便使用.

接下来分享一下其他用法:

比如监视PLC值:(为了方便不写界面): 这样就可以实时观察到PLC的值

import os,sys,time,win32api,win32con,win32gui
def cmd(s="pause"):os.system(s)
from ctypes import *
import clr,System#C语言那一套 拿过来 C#那一套数据类型拿过来
from System import String, Char, Int32,UInt16, Int64, Environment, IntPtr
print(clr.AddReference("HslCommunication"))#导包
import HslCommunication#现在可以当python自己的库来用了
modbus = HslCommunication.ModBus.ModbusTcpNet( "192.168.1.10" );#modbus = HslCommunication.ModBus.ModbusTcpNet( "127.0.0.1" );
while 1:cmd("cls")time.sleep(0.5)for i in range(15):time.sleep(0.3)intReg_100 = modbus.ReadUInt16("100");Reg_100 = intReg_100.Content;intReg_101 = modbus.ReadUInt16("101");Reg_101 = intReg_101.Content;intReg_105 = modbus.ReadUInt16("105");Reg_105 = intReg_105.Content;intReg_106 = modbus.ReadUInt16("106"); Reg_106 = intReg_106.Content;print(f"""{time.strftime("%H:%M:%S")}   \n\nD100:{Reg_100}  D101:{Reg_100}  D105:{Reg_100}  D106:{Reg_100}   \n""")#intReg_100.IsSuccess,intReg_100.Content, #     intReg_101.IsSuccess,intReg_101.Content,#     intReg_105.IsSuccess,intReg_105.Content,#     intReg_106.IsSuccess,intReg_106.Content)>#pip install pywin32 numpy pythonnet -i https://pypi.tuna.tsinghua.edu.cn/simple

下来在分享一个socket的脚本 用来调试VM很方便.


def p(P):print(P);
import os,sys,time,socket,_thread,threading关闭时间=60*2
from threading import Timer
def close_window():print("cl")os._exit(0)#root.destroy()  # 销毁主窗口#print("窗口已关闭")
Timer(关闭时间, close_window).start()#def thread_it(func, *args):t = threading.Thread(target=func, args=args)t.setDaemon(True);t.start();
def Thread_ConnectSocket(ip="",prot=0,sendData="",recv_flag=True):是否发送成功标志=Falsewhile True:try:global tcp_client_sockettcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_client_socket.connect((ip, prot))while True:try:tcp_client_socket.send(sendData.encode("utf-8"))#发送print(f'>>{ip}:({prot})"{sendData}"!')if (recv_flag):#print(f'等待对方回复!')recv_content = tcp_client_socket.recv(1024)#接收result = recv_content.decode("utf-8")print(f'收到对方发来的"{result}"!')   print(f'done!')是否发送成功标志=Truebreakexcept:是否发送成功标志=Falsebreakexcept socket.error:print("未连接,尝试重连中..")time .sleep(1)if (是否发送成功标志):print(f'finish!')breakipport=("127.0.0.1", 7930)#右侧IP=ipport[0]
PROT=ipport[1] #IP,PROT def Tcp_Send(S=""):#B_左(7931)thread_it(Thread_ConnectSocket,IP,PROT,S)import tkinter as tkroot = tk.Tk()#root.iconbitmap('mfc.ico')
root.geometry('400x400+100+100')
#root.resizable(0,0)
root.title("")
lb__a = tk.Label(root,text=f"(7931) :{IP} : {PROT}",fg='green',font=("微软雅黑",16))
lb__a.pack()def gettime():dstr.set(time.strftime("%H:%M:%S"))try:passroot.after(1000, gettime)# 每隔 1s 调用一次.except:passdstr = tk.StringVar()# 定义动态字符串
lb = tk.Label(root,textvariable=dstr,fg='green',font=("微软雅黑",20))
lb.pack()
gettime()# 0 800V 一体轭
# 1 800V 中柱
# 2 800V 边柱
##------------------------------料号0---------------------------------------------------------
#---------------------------------------------------------------------------------------------
def Cam0():Tcp_Send("0") 
button = tk.Button(root,text=' 料号0:800V 一体轭',bg='#7CCD7C',width=20, height=2,command=Cam0)
button.place(relx=0.15,rely=0.2, width=260, height=30)#------------------------------料号1---------------------------------------------------------
#---------------------------------------------------------------------------------------------def Cam1():Tcp_Send("1")#Tcp_SendB Tcp_SendA #B_左(7931)  A_右(7930) 
button = tk.Button(root,text='料号1:800V 中柱',bg='#7CCD7C',width=20, height=2,command=Cam1)
#button.pack()
button.place(relx=0.15,rely=0.3, width=260, height=30)#------------------------------料号2---------------------------------------------------------
#---------------------------------------------------------------------------------------------def Cam2():Tcp_Send("2")#Tcp_SendB Tcp_SendA #B_左(7931)  A_右(7930) 
button = tk.Button(root,text='料号2:800V 边柱',bg='#7CCD7C',width=20, height=2,command=Cam2)
#button.pack()
button.place(relx=0.15,rely=0.4, width=260, height=30)#------------------------------料号:    噢噢噢噢---------------------------------------------------------
#---------------------------------------------------------------------------------------------def CamT1():Tcp_Send("T1")
button = tk.Button(root,text='拍照1',bg='#7CCD7C',width=20, height=2,command=CamT1)
button.place(relx=0.15,rely=0.5, width=260, height=30)def CamT2():Tcp_Send("T2")
button = tk.Button(root,text='拍照2',bg='#7CCD7C',width=20, height=2,command=CamT2)
button.place(relx=0.15,rely=0.6, width=260, height=30)def CamT3():Tcp_Send("T3")
button = tk.Button(root,text='拍照3',bg='#7CCD7C',width=20, height=2,command=CamT3)
button.place(relx=0.15,rely=0.7, width=260, height=30)def CamT4():Tcp_Send("T4")
button = tk.Button(root,text='拍照4',bg='#7CCD7C',width=20, height=2,command=CamT4)
button.place(relx=0.15,rely=0.8, width=260, height=30)#---------------------------------------------------------------------------------------------
#------------------------------料号2---------------------------------------------------------
#---------------------------------------------------------------------------------------------root.mainloop()

虽然是工作用到的,但只是自己测试工具;放出来也不影响..

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

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

相关文章

docker构建镜像并上传dockerhub

docker构建镜像并上传dockerhub 前提条件&#xff1a;需要连接梯子 将梯子配置到虚拟机中&#xff08;确保主机能够连接 hub.docker.com&#xff09; 使用ipconfig 查询主机的 ip4地址虚拟机的连接模式改成桥接模式&#xff08;复制主机的地址网络&#xff09;将ip4配置到虚拟…

python实现的音乐播放器

python实现的音乐播放器 音乐播放器,原来写过一个简陋的例子,可见 https://blog.csdn.net/cnds123/article/details/137874107 那个不能拖动播放进度条上的滑块到新的位置播放。下面介绍的可以拖动播放进度条上的滑块到新的位置播放。 简单实用的音乐播放器 这个简单实用的…

[网安工具] 端口信息收集工具 —— 御剑高速 TCP 全端口扫描工具 · 使用手册

&#x1f31f;想了解其它网安工具&#xff1f;看看这个&#xff1a;[网安工具] 网络安全工具管理 —— 工具仓库 管理手册 https://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scannerhttps://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scanner 0…

数字孪生赋能智慧城市:从概念到落地的深度实践

在城市规模与复杂度持续攀升的当下&#xff0c;传统管理模式已难以满足现代城市精细化治理需求。数字孪生技术凭借构建虚拟城市镜像、实现实时数据交互与智能决策的特性&#xff0c;成为智慧城市建设的核心引擎。本文将通过多个典型案例&#xff0c;深度解析数字孪生技术如何重…

DeFi开发系统软件开发:技术架构与生态重构

DeFi开发系统软件开发&#xff1a;技术架构与生态重构 ——2025年去中心化金融开发的范式革新与实践指南 一、技术架构演进&#xff1a;从单一链到多链混合引擎 现代DeFi系统开发已从单一公链架构转向“跨链互操作混合模式”&#xff0c;结合中心化效率与去中心化安全双重优势…

相同IP和端口的服务器ssh连接时出现异常

起因 把服务器上的一个虚拟机搞坏了&#xff0c;所以删除重新创建了一个&#xff0c;端口号和IP与之前的虚拟机相同。 ssh usernameIP -p port 时报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone…

验证es启动成功

1. 查看命令行输出信息 在启动 Elasticsearch 时&#xff0c;命令行窗口会输出一系列日志信息。若启动成功&#xff0c;日志里通常会有类似下面的信息&#xff1a; plaintext [2025-05-06T13:20:00,000][INFO ][o.e.n.Node ] [node_name] started其中 [node_na…

CentOS网络之network和NetworkManager深度解析

文章目录 CentOS网络之network和NetworkManager深度解析1. CentOS网络服务发展历史1.1 传统network阶段&#xff08;CentOS 5-6&#xff09;1.2 过渡期&#xff08;CentOS 7&#xff09;1.3 新时代&#xff08;CentOS 8&#xff09; 2. network和NetworkManager的核心区别3. ne…

Unity:父挂 Rigidbody2D、子挂 Collider2D 时触发器不生效的问题分析

目录 ❓问题现象 &#x1f50d; 排查与定位 ⚠️ Unity 触发机制的核心要求 ✅ 为什么把 Collider2D 移到父物体后就能触发&#xff1f; &#x1f4a1; 解决方案 在 Unity 2D 游戏开发中&#xff0c;很多人习惯用父物体挂载 Rigidbody2D&#xff0c;而将不同的身体部位&am…

Google AI版图:解析AI Studio, Gemini, NotebookLM与GCP

1. 2C vs 2B: AI Studio: 主要是面向开发者&#xff0c;提供一个易用的界面来探索和构建基于Google模型的应用。虽然最终的应用可能服务于C端或B端&#xff0c;但AI Studio本身更多是一个开发者的工具平台&#xff0c;可以看作是连接模型能力和各种应用的桥梁。它可以被个人开…

Oracle EBS AP发票被预付款核算创建会计科目时间超长

背景 由于客户职能部门的水电、通信和物业等等费用统一管理或对接部门报销费,在报销费的时候,用户把所有费用分摊到各个末级部门,形成AP发票行有上千行, 问题症状 1、用户过账时,请求创建会计科目一直执行20多个小时未完成,只能手工强行取消请求。 2、取消请求以后,从后…

MySQL中MVCC指什么?

简要回答&#xff1a; MVCC&#xff08;multi version concurrency control&#xff09;即多版本并发控制&#xff0c;为了确保多线程下数据的安全&#xff0c;可以通过undo log和ReadView来实现不同的事务隔离级别。 对于已提交读和可重复读隔离级别的事务来说&#xff0c;M…

赛季7靶场 -- Checker --User flag

本系列仅说明靶场的攻击思路&#xff0c;不会给出任何的详细代码执行步骤&#xff0c;因为个人觉得找到合适的工具以实现攻击思路的能力也非常重要。root要逆向&#xff0c;没做了&#xff0c;但是user flag也有借鉴意义&#xff0c;关于2FA的绕过我们有必要了解 1.首先Nmap扫描…

【RAG技术全景解读】从原理到工业级应用实践

目录 &#x1f31f; 前言&#x1f3d7;️ 技术背景与价值&#x1f6a8; 当前技术痛点&#x1f6e0;️ 解决方案概述&#x1f465; 目标读者说明 &#x1f50d; 一、技术原理剖析&#x1f4d0; 核心概念图解&#x1f4a1; 核心作用讲解⚙️ 关键技术模块说明⚖️ 技术选型对比 &…

【嵌入式开发-RS-485】

嵌入式开发-RS-485 ■ RS-485 连接方式■ RS-485 半双工通讯■ RS-485 的特点■ UART硬流控■ RS-4851. 全双工、半双工接线2. 拓扑结构3. RS-485收发器3.1 发送模式&#xff08;TX&#xff09;3.2 接收模式&#xff08;RX&#xff09; 4. RS-485数据链路5. RS-485常用电路6. C…

[硬件电路-18]:MCU - LPC1765FBD100是恩智浦(NXP)半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器

LPC1765FBD100是恩智浦&#xff08;NXP&#xff09;半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器&#xff0c;具备高集成度、低功耗、丰富的外设接口和强大的处理能力&#xff0c;适用于工业控制、消费电子、医疗设备、通信系统等嵌入式应用场景。 以下从核心特…

MyBatis(进阶)(xml标签)

本节⽬标 1. 学习MyBatis的动态SQL查询 2. 掌握MyBatis在项⽬中的应⽤, 可以使⽤Spring MVC完成⼀些基础的功能 1. 动态SQL&#xff08;XML&#xff09; 动态 SQL 是Mybatis的强⼤特性之⼀&#xff0c;能够完成不同条件下不同的 sql 拼接 可以参考官⽅⽂档&#xff1a; M…

QT QList容器及行高亮

总结QList是一个泛型/模板链表&#xff0c;可以自己定义数据类型&#xff0c;ExtraSelection是一种“数据类型”

【Python】Python项目中的依赖与配置:requirements.txt、setup.py、pyproject.toml 详解

在昨天的文章【Python】通过Editable Install模式详解&#xff0c;解决Python开发总是import出错的问题 中&#xff0c; 我们提到了Python项目的配置文件requirements.txt、setup.py、pyproject.toml。在昨天的解决方案中&#xff0c; 我们同时维护了这三个文件。 由同学就问&a…

详细聊聊 Synchronized,以及锁的升级过程

在Java中&#xff0c;synchronized关键字是用于实现线程同步的重要机制&#xff0c;它通过内置锁&#xff08;Monitor&#xff09;确保多个线程对共享资源的安全访问。 1. synchronized 的基本使用与实现原理 使用方式 修饰实例方法&#xff1a;锁是当前对象实例。public syn…