当前位置: 首页 > news >正文

巧用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()

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

相关文章:

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

工作中用到了VM(VisionMaster4.3)有时候需要和PLC打交道,但是PLC毕竟是别人的,不方便修改别人的程序,这时候需要一个灵活的PLC模拟器是多么好呀! 先说背景: PLC型号 汇川Easy521: Modbus TCP 192.168.1.10:502 在汇川Easy521中Modbus保持寄存器D寄存器 ,在modbus协议中 0-4区…...

Nginx +Nginx-http-flv-module 推流拉流

这两天为了利用云服务器实现 Nginx 进行OBS Rtmp推流&#xff0c;Flv拉流时发生了诸多情况&#xff0c;记录实现过程。 环境 OS&#xff1a;阿里云CentOS 7.9 64位Nginx&#xff1a;nginx-1.28.0Nginx-http-flv-module&#xff1a;nginx-http-flv-module-1.2.12 安装Nginx编…...

DRF+Vue项目线上部署:腾讯云+Centos7.6

1.服务器选购和配置 1.1.服务器选购 1.2.防火墙/安全组配置 80是HTTP的端口&#xff0c;443是HTTPS的端口&#xff0c;22是远程连接的端口&#xff0c;3306是mysql的端口。 1.3.远程连接软件&#xff08;tabby&#xff09;配置 下载&#xff1a;https://github.com/Eugeny/ta…...

大疆无人机(全系列,包括mini)拉流至电脑,实现直播

参考视频 【保姆级教程】大疆无人机rtmp推流直播教程_哔哩哔哩_bilibili VLC使用教程&#xff1a; VLC工具使用指南-CSDN博客 目录 实现效果&#xff1a; 电脑端 ​编辑 ​编辑 无人机端 VLC拉流 分析 实现效果&#xff1a; (实验机型&#xff1a;大疆mini4kRC-N2遥控器、大…...

Spring普通配置类 vs 自动配置类-笔记

1.简要版 Configuration和Bean&#xff0c;既可以用于普通配置类&#xff0c;也可以用于自动配置类。二者的区别和联系是什么呢&#xff1f; 区别&#xff1a; Configuration和Bean是Spring框架本身的注解&#xff0c;用于定义配置类和生成Bean。而自动配置通常是Spring Boo…...

解决Ceph 14.2.22 Nautilus版本监视器慢操作问题的实践指南

解决Ceph Nautilus版本监视器慢操作问题的实践指南 问题背景问题现象问题分析1. 确认监视器状态2. 检查慢操作详情3. 深入分析操作状态 问题原因解决方案立即解决方法 总结 在生产环境中执行任何操作前&#xff0c;请确保已备份重要数据&#xff0c;并在测试环境中验证解决方案…...

Go使用Gin写一个对MySQL的增删改查服务

首先用SQL创建一个包含id、name属性的users表 create table users (id int auto_incrementprimary key,name varchar(255) null );查询所有用户信息&#xff1a; func queryData(db *sql.DB, w http.ResponseWriter) {rows, err : db.Query("SELECT * FROM users"…...

数字孪生医疗:构建患者特异性数字孪生体路径探析

引言 数字孪生技术正逐渐成为医疗健康领域的一场革命性力量,它通过创建人体器官和系统的数字复制品,为疾病预测、诊断和个性化治疗开辟了前所未有的可能性。在心血管疾病领域,数字孪生技术的应用尤为引人注目,特别是对于扩张型心肌病(Dilated Cardiomyopathy,DCM)这一常…...

rust程序静态编译的两种方法总结

1. 概述 经过我的探索&#xff0c;总结了两种rust程序静态编译的方法,理论上两种方法都适用于windows、mac os和linux(mac os未验证)&#xff0c;实测方法一性能比方法二好&#xff0c;现总结如下&#xff0c;希望能够帮到你. 2.方法一 2.1 添加配置文件 在项目的同级文件夹下新…...

threejs 添加css3d标签 vue3

如图所示&#xff0c;给收费站标注标签。步骤如下&#xff1a; 一、引入 import {CSS3DRenderer,CSS3DObject, } from "three/examples/jsm/renderers/CSS3DRenderer"; 二、 //一、申明css3渲染器 const label3DRenderer ref()//二、创建css3d渲染器--将渲染器加…...

【git】git fsmonitor

git fsmonitor 是 Git 提供的一个特性&#xff0c;用于提高 Git 的性能&#xff0c;尤其是在处理文件状态检查&#xff08;git status&#xff09;时。它通过文件系统监控服务&#xff08;如 Windows 上的 FileSystemWatcher 或 Linux 上的 inotify&#xff09;来跟踪文件系统的…...

开源照片管理系统PhotoPrism的容器化部署与远程管理配置

文章目录 前言1.关于PhotoPrism2.本地部署PhotoPrism3.PhotoPrism简单使用4. 安装内网穿透5.配置PhotoPrism公网地址6. 配置固定公网地址 前言 你是不是用手机拍照片的时候无所谓拍了多少张&#xff0c;但在整理的时候却要占用超多时间&#xff1f;别怕&#xff0c;今天给大家…...

macOS Python 环境配置指南

1. 检查现有 Python 环境 python3 --version # 检查 Python 3 版本 pip3 --version # 检查 pip 版本 2. 安装 pyenv&#xff08;Python 版本管理工具&#xff09; # 使用 Homebrew 安装 pyenvbrew install pyenv# 配置 pyenv 环境变量&#xff08;添加到 ~/.zshrc&#…...

如何阅读、学习 Git 核心源代码 ?

学习 Git 核心源代码是一个深入理解版本控制系统底层原理的绝佳方式。以下是分阶段的系统性建议&#xff0c;结合了实践经验和学习路径设计&#xff1a; 一、前置知识储备 C语言进阶 重点掌握指针操作&#xff08;尤其是二级指针和函数指针&#xff09;结构体嵌套与内存对齐…...

学习黑客 MAC 地址深入了解

在江湖之中&#xff0c;每位少侠既有“门派”加身&#xff0c;也需持有“令牌”通行。MAC地址便是我被师门铁订在骨血里的“先天武功心法”&#xff0c;铸于出厂之时&#xff0c;终身不变&#xff0c;只有破阵高手才能暗度陈仓才能改写&#xff1b;而IP地址则是我行走江湖的“后…...

长事务:数据库中的“隐形炸弹“——金仓数据库运维避坑指南

引言&#xff1a;凌晨三点的告警 "张工&#xff01;生产库又告警了&#xff01;"凌晨三点的电话铃声总是格外刺耳。运维团队发现数据库频繁进入单用户模式&#xff0c;排查发现某核心表的年龄值&#xff08;Age&#xff09;已突破20亿大关。经过一夜奋战&#xff0c…...

WTK6900C-48L:离线语音芯片重构玩具DNA,从“按键操控”到“声控陪伴”的交互跃迁

一&#xff1a;开发背景 随着消费升级和AI技术进步&#xff0c;传统玩具的机械式互动已难以满足市场需求。语音控制芯片的引入使玩具实现了从被动玩耍到智能交互的跨越式发展。通过集成高性价比的语音识别芯片&#xff0c;现代智能玩具不仅能精准响应儿童指令&#xff0c;还能实…...

一文读懂Python之requests模块(36)

一、requests模块简介 requests模块是python中原生的一款基于网络请求的模块&#xff0c;功能强大&#xff0c;简单便捷且高效 &#xff0c;该模块可以模拟浏览器发送请求&#xff0c;主要包括指定url、发起请求、获取响应数据和持久化存储&#xff0c;包括 GET、POST、PUT、…...

Java 异常

Java 异常的体系结构 java.lang.Throwable ├── Error&#xff08;严重错误&#xff0c;程序无法处理&#xff09; │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── ... └── Exception&#xff08;程序可以处理的异常&#xff09;├── Check…...

智能外呼机器人的核心优势

随着人工智能技术的快速发展&#xff0c;智能外呼机器人已成为企业降本增效的重要工具。其通过整合语音识别&#xff08;ASR&#xff09;、自然语言处理&#xff08;NLP&#xff09;、语音合成&#xff08;TTS&#xff09;等技术&#xff0c;不仅替代了大量重复性人工劳动&…...

n8n - 开放灵活的智能自动化工作流平台

n8n - 开放灵活的智能自动化工作流平台 在当今数字化转型的大潮中,自动化已成为众多企业提升效率、优化流程的关键手段。而在众多自动化工具中,n8n以其开放灵活且强大的功能脱颖而出,成为技术团队的可靠选择。今天,我们将深入了解n8n这个开源自动化工作流平台,探索其广泛…...

window 显示驱动开发-处理内存段(二)

KMD 不需要在其内存段中指定 GPU 可用的所有视频内存资源。 但是&#xff0c;KMD 必须指定 VidMm 在系统上运行的所有进程中管理的所有内存资源。 例如&#xff1a; 实现固定函数管道的顶点着色器微代码可以驻留在 GPU 地址空间中&#xff0c;但在 VidMm 管理的内存之外&#x…...

RSS 2025|斯坦福提出「统一视频行动模型UVA」:实现机器人高精度动作推理

导读 在机器人领域&#xff0c;让机器人像人类一样理解视觉信息并做出精准行动&#xff0c;一直是科研人员努力的方向。今天&#xff0c;我们要探讨的统一视频行动模型&#xff08;Unified Video Action Model&#xff0c;UVA&#xff09;&#xff0c;就像给机器人装上了一个“…...

代码随想录算法训练营第60期第三十天打卡

大家好&#xff0c;今天我们要走进一个全新的章节&#xff0c;这一章叫做贪心算法&#xff0c;前面我们讲的是回溯算法&#xff0c;那究竟什么是贪心算法呢&#xff1f;我们一起走进今天的内容。 第一部分贪心的理论基础 其实大家看这个名字估计也会有一定了解&#xff0c;贪心…...

腾讯云:数字世界的“量子熔炉”与硅基文明引擎​

​​一、算力拓扑学&#xff1a;重新定义空间的计算密度​​ 腾讯云的算力网络正在突破经典物理限制&#xff0c;其分布式架构通过“量子化”资源调度实现超维计算&#xff1a; ​​虚拟化跃迁​​&#xff1a;基于KVM的轻量级虚拟化技术&#xff0c;将单台物理服务器切割为百…...

Python Cookbook-7.7 通过 shelve 修改对象

任务 你正在使用标准库模块shelve。你用shelve处理过的一些值是易变的对象(mutableobjects)&#xff0c;而且你需要修改这些对象。 解决方案 shelve 模块提供了一种持久的字典——在强大的关系型数据库和简洁的 marshal、pickledbm 以及类似的文件格式之间&#xff0c;它有着…...

Baklib构建AI就绪知识管理体系

Baklib构建AI就绪知识体系 在数字化转型加速的背景下&#xff0c;Baklib通过其AI就绪知识管理体系&#xff0c;为企业提供了从数据整合到智能应用的完整解决方案。该平台以知识中台为核心架构&#xff0c;依托自然语言处理与机器学习技术&#xff0c;对分散在企业文档系统、协…...

嵌入式开发学习日志Day16

一、指针函数 函数的返回值为指针的函数&#xff1b; 注意&#xff1a;不能返回局部变量的地址&#xff1b; 可以返回静态变量的地址&#xff1b; 可以返回全局变量的地址&#xff1b; 1、动态内存分配 void *malloc(size_t size); //申请空间的函数 void free(*ptr); …...

SLAM文献之KernelGPA: A Globally Optimal Solution to Deformable SLAM in Closed-form

KernelGPA: A Globally Optimal Solution to Deformable SLAM in Closed-form 提出了一种在非刚性变形环境下求解 SLAM 问题的闭式全局最优解方法。下面是对其算法原理和核心推导过程的系统解析。 一、算法背景与目标 问题描述&#xff1a; 传统 SLAM 主要假设环境为刚性&am…...

C++之“继承”

继续开始关于C相关的内容。C作为面向对象的语言&#xff0c;有三大特性&#xff1a;封装&#xff0c;继承&#xff0c;多态。 这篇文章我们开始学习&#xff1a;继承。 一、继承的概念和定义 1. 继承的概念 什么是继承呢&#xff1f; 字面意思理解来看&#xff1a;继承就是…...

java集成telegram机器人

java集成telegram机器人 最近项目需要集成telegram机器人&#xff0c;来实现消息推送功能&#xff0c;以此记录下。 1.创建telegram账号 没有账号的可以去某宝上买一个&#xff0c;千万不要用自己的手机号86去注册&#xff0c;你懂得。 2. 打开BotFather对话创建机器人获取…...

从代码学习深度学习 - 单发多框检测(SSD)PyTorch版

文章目录 前言工具函数数据处理工具 (`utils_for_data.py`)训练工具 (`utils_for_train.py`)检测相关工具 (`utils_for_detection.py`)可视化工具 (`utils_for_huitu.py`)模型类别预测层边界框预测层连接多尺度预测高和宽减半块基础网络块完整的模型训练模型读取数据集和初始化…...

因子分析——数学原理及R语言代码

这里写自定义目录标题 因子分析参数估计方法主成分法主因子法 因子旋转 代码实现Reference 因子分析 FactorAnalysis的目的是从多个高度相关的观测变量中提取出少数几个LatentFactor&#xff0c;这些因子代表了变量背后的共通结构&#xff0c;从而实现降维并提升可解释性。 假…...

CSS3 过渡与动画

在现代网页设计中&#xff0c;平滑的过渡和生动的动画效果已成为提升用户体验不可或缺的元素。CSS3 为我们提供了强大的 transition 和 animation 属性&#xff0c;让开发者能够轻松实现各种视觉效果。本文将深入探讨这两大功能的特性和应用场景。 一、CSS3 过渡&#xff08;T…...

【JAVA】【重试间隔】多线程中两种常见的重试间隔

一、前言 报&#xff01;&#xff01; 小南啊&#xff0c;今日有个小任务交给你去办。就是程序中有个数据处理&#xff0c;总是会出错&#xff0c;不知道是什么原因&#xff0c;你去处理一下&#xff01; 二、主题 围绕数据处理问题去看&#xff0c;从中发现&#xff0c;是因为…...

在现代Web应用中集成 PDF.js (pdfjs-dist 5.2 ESM): 通过 jsdelivr 实现动态加载与批注功能的思考

PDF 文档在现代 Web 应用中越来越常见&#xff0c;无论是作为文档预览、报告展示还是在线编辑的载体。Mozilla 的 PDF.js 是一个功能强大的 JavaScript 库&#xff0c;它使得在浏览器端渲染和显示 PDF 文件成为可能&#xff0c;无需依赖原生插件。 本文将深入探讨如何在你的项…...

android ViewModel liveData无法监听之多线程下activityViewModels不安全

我们一般的&#xff0c;会遇到liveData无法监听到结果&#xff0c;可能存在主要2种可能&#xff1a; liveData没有正确注册&#xff1b;liveData连续多次设置值&#xff0c;中间的值&#xff0c;会被丢弃&#xff0c;但最后一次是能监听到的。 但是我们容易忽略一种case&…...

【即插即用涨点模块】DSConv动态蛇形卷积:自适应聚焦细长弯曲的局部结构特征,助力分割高效提点【附源码+注释】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

守护数字家园:个人博客安全防护指南

前言 在之前的文章《WordPress个人博客搭建&#xff08;一&#xff09;》《WordPress个人博客搭建&#xff08;二&#xff09;》《WordPress个人博客搭建&#xff08;三&#xff09;》中&#xff0c;我们已经在非凡云云服务器上&#xff0c;借助1Panel搭建起属于自己的数字庭院…...

课外活动:简单了解原生测试框架Unittest前置后置的逻辑

简单了解原生测试框架Unittest前置后置的逻辑 一、测试框架执行顺序解析 1.1 基础执行流程 import unittestclass A(unittest.TestCase):classmethoddef setUpClass(cls):print(f"【CLASS START】{cls.__name__}")def setUp(self):print(f"【TEST START】{se…...

带你玩转 Flink TumblingWindow:从理论到代码的深度探索

0.前言 在深入探讨 TumblingWindow 之前&#xff0c;我们先来了解一下流处理或流计算中“窗口”的基本概念。在数据流中&#xff0c;源会持续不断地生成数据&#xff0c;因此计算最终值是不可行的。 在大多数用例中&#xff0c;为了获取有意义的信息&#xff0c;最好使用两种方…...

Java线程安全问题深度解析与解决方案

一、线程安全问题的本质 并发编程的核心挑战&#xff1a;当多个线程同时访问共享资源时&#xff0c;由于操作系统的抢占式调度特性&#xff0c;可能导致不可预期的结果。这种因非原子操作和竞态条件引发的数据不一致问题&#xff0c;称为线程安全问题。 二、经典线程安全问题案…...

python实现的音乐播放器

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

SMT贴片工艺核心优化与生产实践

内容概要 作为现代电子制造的核心环节&#xff0c;SMT贴片工艺的优化直接决定了产品可靠性与生产效率。本文系统性梳理工艺链中的关键控制点&#xff0c;从锡膏印刷精度到回流焊温度曲线&#xff0c;再到AOI检测技术升级&#xff0c;形成覆盖全流程的优化框架。针对行业普遍存…...

趣味编程:爱心

概述&#xff1a;五月十一号就是母亲节了&#xff0c;本篇博客主要是为母亲所写&#xff0c;这是属于程序员的浪漫&#xff0c;这篇博客是对母亲这么多年无微不至爱的情书。 目录 1. 效果展示 2. 源码展示 3. 代码逻辑详解 3.1 头文件与常量定义 3.2 心形曲线参数方程 3.…...

C语言—指针2

1. const 修饰变量 1.1 const修饰变量 变量被const修饰时&#xff0c;变量此时为常变量&#xff0c;本质为常量&#xff0c;语法上不可被修改&#xff0c;但是如果此时需要修改变量值&#xff0c;可以通过指针的方式修改。 虽然此时通过指针的方式确实修改了变量的值&#xff…...

66、微服务保姆教程(九)微服务的高可用性

微服务的高可用性与扩展 服务的高可用性 集群搭建与负载均衡。服务的故障容错与自愈。分布式事务与一致性 分布式事务的挑战与解决方案。使用 RocketMQ 实现分布式事务。微服务的监控与可观测性 metrics 和日志的收集与分析。sentinel 的监控功能。容器化与云原生 将微服务部署…...

主场景 工具栏 植物卡牌的渲染

前置知识&#xff1a;使用easyx图形库 1.IMAGE内存变量存储的是一张位图(图像)&#xff0c;存储了像素数据(颜色&#xff0c;尺寸等) 2.loadimage(&变量名&#xff0c;"加载的文件路径")表示从文件中加载图像到变量中 3. saveimage("文件路径", &变…...

超详细!RxSwift 中的 BehaviorRelay 使用教程(含原理 + 示例 + 实战)

目录 前言 1.什么是 BehaviorRelay 2.基本使用方式 3.BehaviorRelay的常用API 4.BehaviorRelay 和其它类型的对比 5.BehaviorRelay的使用场景 1.绑定UITableView 2.MVVM 场景下使用 BehaviorRelay 6.使用注意事项以及建议 1.注意事项 2.使用建议总结 7.推荐阅读 前…...

【软件测试学习day7】Junit5

Junit 是单元测试框架&#xff0c;本期掌握 Junit5 的基础用法。 1. 注解 首先引入 Junit 依赖&#xff1a; <dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.1</version…...