感谢 TurboAI对本博客的的大力赞助。 创作不易,如果您觉得有帮助,请 支持LIncol29! 为了让我能够继续创作更好的内容,你也可以选择订阅博客的 VIP ,包年VIP仅需10元/年,所有VIP内容免费观看
公司选择:自动化大厂
汇川
大族激光
拓邦(双休)
慧灵机械人(公积金最高比例)
海目星激光
吉阳智能
英威腾
长盈精密技术
无锡先导
东莞思榕智能装备
苏州博众精工
实际问题
Modbus
底层交互逻辑: 主从模式通讯,主机发送信息,从机接受
Modbus网络上只能有一个主站存在,主站在 Modbus网络上没有地址,从站的地址范围为 0 – 247,其中 0 为广播地址,从站的实际地址范围为 1 – 247。Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。
通讯格式:波特率,检验方式,数据位,停止位
Modbus RTU:
帧结构 = 地址 + 功能码+ 数据 + 校验
- 地址: 占用一个字节,范围0-255,其中有效范围是1-247,其他有特殊用途,比如255是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复)。
- 功能码:占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能。
- 数据:根据功能码不同,有不同结构,在下面的实例中有说明。
- 校验:为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了。
- 发送:从机的地址+我要干嘛的功能码+我要查的寄存器的地址+我要查的寄存器地址的个数+校验码
- 回复:从机的地址+主机发我的功能码+要发送给主机数据的字节数+数据+校验码
如何使用Modbus TCP协议与PLC通讯
- 获取PLC的地址和端口号
- 选择NModbus库
- 创建Modbus TCP连接,连接需要指定PLC的IP地址和端口号(一般为502)
- 读写寄存器,PLC的数据一般保存在寄存器中,使用读取输入寄存器或者保持寄存器等
Socket
WPF
EFCore
数据更新显示在界面上
public partial class Form1 : Form
{
// 定义用于跨线程更新 UI 的委托
private delegate void UpdateUIDelegate(string data);
public Form1()
{
InitializeComponent();
}
private void UpdateUI(string data)
{
// 确保在 UI 线程上执行
if (textBox1.InvokeRequired)
{
//Invoke方法里面需要传入委托,data是UpdateUI需要传入的参数
textBox1.Invoke(new UpdateUIDelegate(UpdateUI), data);
}
else
{
// 在 UI 上更新控件
textBox1.Text = data;
}
}
private void SimulateThread()
{
// 模拟线程中的操作
for (int i = 0; i < 10; i++)
{
// 模拟获取数据
string data = $"Data from Thread: {i}";
// 调用 UpdateUI 方法,确保在 UI 线程上执行
UpdateUI(data);
// 模拟线程休眠
Thread.Sleep(1000);
}
}
private void btnStartThread_Click(object sender, EventArgs e)
{
// 启动线程
Thread thread = new Thread(SimulateThread);
thread.Start();
}
}
将Form2的数据传输到Form1
Form1窗体代码:
private void Button1_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.showEvent += Show; //注册事件,只能+= 必须放在ShowDialog()前面
form2.ShowDialog();
}
private int Show(int arg)
{
textBox1.Text = arg.ToString();
return 1;
}
Form2窗体代码:
public event Func<int, int> showEvent; //声明事件委托变量f,类型为int,返回值为int
//form2按钮事件
private void Button1_Click(object sender, EventArgs e)
{
//给form1窗体的文本框赋值20
if (showEvent != null) //判断事件是否为空
{
showEvent(20); //触发事件
}
}
为什么要使用EF框架(ORM框架连接数据库)
- 不必直接处理数据库表和SQL查询。可以将数据库的表映射到程序中的对象,使用C#处理
- 简化数据库操作:提供了强大的API,数据库的增删查改更加容易
- 可移植性:不要改变应用程序代码,只需更新EF的配置即可(mysql、sqlserver、postgresql)
- Linq支持
如何与MES通讯
C#
委托与事件
委托:
- 委托一般声明在类的外部
- 将⽅法当作参数传⼊另⼀个⽅法,可以理解为指向一个方法的引用
- 修饰符 + delegate + 返回值类型 + 委托名(参数);
- C#自定义委托有Action<T>,表示引用一个void返回类型的方法。Func<T>,表示引用带返回类型的方法
Func委托、Action委托、Predicate委托
Func委托必须要求所有的接受方法必须要有返回值类型
//前面是形参 最后一个是返回值类型(如果只有一个参数那么则是返回值类型)
Func<int,int,double> func = (a,b) => a + b; //double为返回值类型
Action委托接受没有返回值的方法
应用于:在跨线程访问可视化控件的时候经常访问
//返回值类型为void
Action<string> act = a => {"你好啊!{0}",a};
Predicate委托是固定bool类型
//返回值类型固定为bool
public delegate bool Predicate<T> (T obj);
(1).BeginInvoke
一个独立的线程上执行所引用方法,并且立即返回到原始线程。原始线程可以继续,所引用方法则在线程池中与原始线程并行执行。
(2).EndInvoke
可在由BeginInvoke新起的线程完成之后获取返回结果,方法是检查BeginInvoke返回的IAsyncResult的IsCompleted属性,或者调用委托的EndInvoke方法来等待委托执行完成。
(3).Invoke和BeginInvoke
Invoke或者BeginInvoke方法都需要一个委托对象作为其参数,
Invoke类似于使用SendMessage方法来给界面线程发送消息,是一个同步方法,不拿到结果就阻塞在那里了。
使用委托实现主从窗体通信
从窗体发送信息—->主窗体接受信息
主窗体发送信息—->从窗体接受信息
委托与事件的对比
事件
- 先定义委托
- 定义事件 修饰符 + event + 委托名 + 事件名
- 注册事件 将事件和处理方法绑在一起 事件+=方法
- 触发事件 判断事件是否为空 不为空则执行
用户控件
用户自己将控件组合。控件中的字体必须和父容器的字体一样。不然不兼容
winform自定义控件
当你自定义一个按钮控件时,通常你会定义自己的外观和交互逻辑。
OnPaint方法:
- 当鼠标进入或离开控件区域时,也可能引起 OnPaint 方法的调用
- 界面发生变化时 ??Form窗体变化不会引起OnPaint方法
- 按钮被遮挡
- 调用Invalidate()方法
using System;
using System.Drawing;
using System.Windows.Forms;
public class CustomButton : Control
{
// 构造函数
public CustomButton()
{
// 设置一些默认值
this.BackColor = Color.LightGray;
this.ForeColor = Color.Black;
this.Size = new Size(100, 40);
// 注册鼠标事件
this.MouseEnter += CustomButton_MouseEnter;
this.MouseLeave += CustomButton_MouseLeave;
this.MouseDown += CustomButton_MouseDown;
this.MouseUp += CustomButton_MouseUp;
}
// 鼠标进入时改变背景颜色
private void CustomButton_MouseEnter(object sender, EventArgs e)
{
this.BackColor = Color.Gray;
}
// 鼠标离开时还原背景颜色
private void CustomButton_MouseLeave(object sender, EventArgs e)
{
this.BackColor = Color.LightGray;
}
// 鼠标按下时改变背景颜色
private void CustomButton_MouseDown(object sender, MouseEventArgs e)
{
this.BackColor = Color.DarkGray;
}
// 鼠标释放时还原背景颜色
private void CustomButton_MouseUp(object sender, MouseEventArgs e)
{
this.BackColor = Color.Gray;
}
// 重写绘图逻辑
//当控件首次显示或者大小发生变化时,系统自动调用
//如果想手动调用则 Invalidate()方法
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// 使用Graphics对象绘制按钮的外观
using (Graphics g = e.Graphics)
{
// 填充按钮的背景
g.FillRectangle(new SolidBrush(this.BackColor), ClientRectangle);
// 绘制按钮的文本
StringFormat sf = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
g.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), ClientRectangle, sf);
}
}
}
上述代码创建了一个名为 CustomButton 的自定义按钮控件。你可以在你的 WinForms 项目中使用这个自定义按钮控件,方式类似于使用内置的按钮控件:
private void Form1_Load(object sender, EventArgs e)
{
CustomButton customButton = new CustomButton
{
Text = "Click me!",
Location = new Point(50, 50)
};
this.Controls.Add(customButton);
}
设计模式
发布者-订阅者模式
类与结构体的区别
相同点 都可以实现接⼝
不同点
1.class是引⽤类型,struct是值类型
2.class允许继承、被继承,struct不允许,只能继承接⼝
3.class可以初始化变量,struct不可以
4.class可以有⽆参的构造函数,struct不可以,必须是有参的构造函数,⽽且在有参的构造函数必须初 始化所有成员
使⽤场景
1.Class⽐较适合⼤的和复杂的数据,表现抽象和多级别的对象层次时。
2.Struct适⽤于作为经常使⽤的⼀些数据组合成的新类型,表示诸如点、矩形等主要⽤来存储数据的轻 量级对象时,偏简单值。
3.Struct有性能优势,Class有⾯向对象的扩展优势。
Debug
Console.WriteLine();//控制台应用输出
Debug.WriteLine();//窗体应用在控制台输出
类的多继承问题,如何解决
C#不支持多继承,但提供了其他的一些机制来实现类似的功能
- 接口:实现多个接口
- 组合:一个类可以包含其他类的实例,调用这些实例的方法的获得所需的行为
- 委托和事件
- 模式
委托的多继承效果:
通过委托,一个类可以包含对另一个类的方法的引用,从而在运行时调用这些方法。
// 使用委托将 ClassB 的方法引用传递给 ClassA
MyDelegate myDelegate = objB.MethodB;
// 调用 ClassA 的方法
objA.MethodA();
// 调用 ClassB 的方法
myDelegate();
事件的多继承效果:
事件是建立在委托的基础上的,通过事件,一个类可以在其内部触发其他类的方法。
class Publisher //发布者
{
public event Action MyEvent;
public void RaiseEvent()
{
MyEvent?.Invoke();
}
}
class Subscriber //订阅者
{
public void HandleEvent()
{
Console.WriteLine("Event handled by Subscriber");
}
}
class Program
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
// 订阅事件
publisher.MyEvent += subscriber.HandleEvent;
// 触发事件
publisher.RaiseEvent();
}
}
数据库
MySql怎么提高增删查改(CRUD)的效率
- 增加索引:经常被查询的字段,注意索引并非越多越好,索引过多会增加系统开销
- 分区表:
- RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
- LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
- HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。任何有效的MySQL表达式,只要产生非负整数值,都可以包含在这个函数中。
- KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
- 适当的数据类型:选择适当的数据类型,以减小表的存储空间和提高查询性能。例如,使用INT而不是VARCHAR作为索引列。
- 使用批量操作:使用批量操作来替代逐条操作,减少数据库交互的开销。例如,使用
INSERT INTO 表名(字段1,字段2,字段3) VALUES (值1,值2,值3)
INSERT INTO 表名 VALUES(值1,值2,值3)语法进行批量插入。
- 缓存策略:使用缓存技术,如Memcached或Redis,来缓存频繁读取但不经常更新的数据,减轻数据库的压力。
- 查询优化:确保查询语句是最优语句,避免不必要的连接和子查询。禁止使用select *
催更!!!
佬,这个干了几年了