命名空间:netMarketing.automation.plc.softPlc
功能:三菱FX2N PLC仿真
功能详细说明:
目前暂时只支持三菱PLC fx2n的下列指令:
LD,LDI,LDP,LDF,AND,ANB,ANI,ANDP,ANDF,OR,ORI,ORB,ORP,ORF,OUT,MPS,MPP,MRD,INC,DEC,SET,RST,MOV,XCH,END
这个类是勇哥很早以前开发的,本意是想做一款plc学习软件,后来由于3D场景仿真困难比较大,这个项目就搁浅了。
后来发现这个类还有个意义是用来以PLC梯形图的方式调用封装好的C#的资源,即结合了PLC和C#各自的优点来开发上位机软件。
如果要实现这一点,需要开发梯形图编辑器,这个也是比较困难的事。勇哥在这方面暂时没有精力继续,期待有感兴趣的朋友可以深入研究下去,如果需要的话,勇哥可以提供源代码。
下面的程序是这个类的一个应用。
这个程序的梯型图编辑只是实现了基本的画元件,但是没能实现梯型图超过一屏后的滚动条卷动的处理过程。
另外,梯型图转指令或者指令转梯型图的功能也没能够完成。

下面的代码只是演示程序的主界面的代码,如果你有兴趣的话,完整的演示程序可以找勇哥索要。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections;
namespace simulation
{
public partial class MainForm : Form
{
public Point DownPoint;
public LADDER ladder;
public bool ladderUpdateFlag = false;
private myPanel panMain;
private List<List<LADDER>> ladderBinaryTree;
private drawTools gt;
private simulation sim;
private string codePath = AppDomain.CurrentDomain.BaseDirectory + "code";
private string partImagePath = AppDomain.CurrentDomain.BaseDirectory + "partImg";
public MainForm()
{
panMain = new myPanel(this);
DownPoint = new Point(panMain.startX,panMain.startY);
ladder.ladderParam = "";
ladder.ladderType = partTypeEnum.nopPart;
InitializeComponent();
this.panel1.Controls.Add(this.panMain);
this.panMain.BringToFront();
this.panMain.Dock = DockStyle.Fill;
this.panMain.BorderStyle = BorderStyle.Fixed3D;
this.panMain.MouseDown += new MouseEventHandler(panMain_MouseDown);
this.panMain.MouseDoubleClick += new MouseEventHandler(panMain_MouseDoubleClick);
sim = new simulation();
ladderBinaryTree = new List<List<LADDER>>();
timer1.Start();
}
void panMain_MouseDoubleClick(object sender, MouseEventArgs e)
{
inputPart part = null;
var partNode = panMain.getCurPosLadderNode();
part = new inputPart(partNode.ladderType);
part.PartParam = partNode.ladderParam;
part.ShowDialog();
}
private void Form1_Load(object sender, EventArgs e)
{
if (!Directory.Exists(codePath))
Directory.CreateDirectory(codePath);
if (!Directory.Exists(partImagePath))
Directory.CreateDirectory(partImagePath);
updateProgramList();
Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
gt = new drawTools(this.pbImg.CreateGraphics(), new Pen(Color.Black, 1), bmp);
}
private void updateProgramList()
{
this.lbxProList.Items.Clear();
var files = Directory.GetFiles(codePath, "*.*");
for (int i = 0; i < files.Length; i++)
{
string[] ary1 = files[i].Split('\\');
files[i] = ary1[ary1.Length - 1];
}
this.lbxProList.Items.AddRange(files);
}
private void timer1_Tick(object sender, EventArgs e)
{
IList<string> dMsg = new List<string>();
IList<string> tMsg = new List<string>();
IList<string> cMsg = new List<string>();
for (ushort i = 0; i < 10; i++)
{
dMsg.Add(string.Format("D{0}:{1}",i,sim.GetD(i).Value));
var tuple1=sim.GetT(i);
tMsg.Add(string.Format("T{0}:{1},{2}", i, tuple1.Item1, tuple1.Item2));
var tuple2 = sim.GetC(i);
cMsg.Add(string.Format("C{0}:{1},{2}", i, tuple2.Item1, tuple2.Item2));
ushort index = i;
if (i == 8) index = 10;
if (i == 9) index = 11;
if (sim.GetX(index).OnOff)
{
if (i == 0) btnX0.BackColor = Color.Yellow;
if (i == 1) btnX1.BackColor = Color.Yellow;
if (i == 2) btnX2.BackColor = Color.Yellow;
if (i == 3) btnX3.BackColor = Color.Yellow;
if (i == 4) btnX4.BackColor = Color.Yellow;
if (i == 5) btnX5.BackColor = Color.Yellow;
if (i == 6) btnX6.BackColor = Color.Yellow;
if (i == 7) btnX7.BackColor = Color.Yellow;
if (i == 8) btnX10.BackColor = Color.Yellow;
if (i == 9) btnX11.BackColor = Color.Yellow;
}
else
{
if (i == 0) btnX0.BackColor = SystemColors.ControlLight;
if (i == 1) btnX1.BackColor = SystemColors.ControlLight;
if (i == 2) btnX2.BackColor = SystemColors.ControlLight;
if (i == 3) btnX3.BackColor = SystemColors.ControlLight;
if (i == 4) btnX4.BackColor = SystemColors.ControlLight;
if (i == 5) btnX5.BackColor = SystemColors.ControlLight;
if (i == 6) btnX6.BackColor = SystemColors.ControlLight;
if (i == 7) btnX7.BackColor = SystemColors.ControlLight;
if (i == 8) btnX10.BackColor = SystemColors.ControlLight;
if (i == 9) btnX11.BackColor = SystemColors.ControlLight;
}
if (sim.GetY(index).OnOff)
{
if (i == 0) btnY0.BackColor = Color.Yellow;
if (i == 1) btnY1.BackColor = Color.Yellow;
if (i == 2) btnY2.BackColor = Color.Yellow;
if (i == 3) btnY3.BackColor = Color.Yellow;
if (i == 4) btnY4.BackColor = Color.Yellow;
if (i == 5) btnY5.BackColor = Color.Yellow;
if (i == 6) btnY6.BackColor = Color.Yellow;
if (i == 7) btnY7.BackColor = Color.Yellow;
if (i == 10) btnY10.BackColor = Color.Yellow;
if (i == 11) btnY11.BackColor = Color.Yellow;
}
else
{
if (i == 0) btnY0.BackColor = SystemColors.ControlLight;
if (i == 1) btnY1.BackColor = SystemColors.ControlLight;
if (i == 2) btnY2.BackColor = SystemColors.ControlLight;
if (i == 3) btnY3.BackColor = SystemColors.ControlLight;
if (i == 4) btnY4.BackColor = SystemColors.ControlLight;
if (i == 5) btnY5.BackColor = SystemColors.ControlLight;
if (i == 6) btnY6.BackColor = SystemColors.ControlLight;
if (i == 7) btnY7.BackColor = SystemColors.ControlLight;
if (i == 10) btnY10.BackColor = SystemColors.ControlLight;
if (i == 11) btnY11.BackColor = SystemColors.ControlLight;
}
if (sim.GetM(i).OnOff)
{
if (i == 0) btnM0.BackColor = Color.Yellow;
if (i == 1) btnM1.BackColor = Color.Yellow;
if (i == 2) btnM2.BackColor = Color.Yellow;
if (i == 3) btnM3.BackColor = Color.Yellow;
if (i == 4) btnM4.BackColor = Color.Yellow;
}
else
{
if (i == 0) btnM0.BackColor = SystemColors.ControlLight;
if (i == 1) btnM1.BackColor = SystemColors.ControlLight;
if (i == 2) btnM2.BackColor = SystemColors.ControlLight;
if (i == 3) btnM3.BackColor = SystemColors.ControlLight;
if (i == 4) btnM4.BackColor = SystemColors.ControlLight;
}
}
object[] m = sim.RunningCompilerLogicList.ToList().ConvertAll(s => (object)s).ToArray();
listBox1.BeginUpdate();
listBox1.Items.Clear();
listBox1.Items.AddRange(m);
listBox1.EndUpdate();
toolMsg1.Text ="扫描周期总数:"+sim.CycleCount+ ", 一个扫描周期耗时(毫秒): " + sim.CycleSpeed.ToString();
toolMsg2.Text = panMain.LadderRows + "," + panMain.LadderCols;
this.lbxD.BeginUpdate();
this.lbxD.Items.Clear();
this.lbxD.Items.AddRange(dMsg.ToArray());
this.lbxD.EndUpdate();
this.lbxT.BeginUpdate();
this.lbxT.Items.Clear();
this.lbxT.Items.AddRange(tMsg.ToArray());
this.lbxT.EndUpdate();
this.lbxC.BeginUpdate();
this.lbxC.Items.Clear();
this.lbxC.Items.AddRange(cMsg.ToArray());
this.lbxC.EndUpdate();
}
private void btnX0_Click(object sender, EventArgs e)
{
string name = ((Button)sender).Name;
switch (name)
{
case "btnX5":
sim.SetX(5, !sim.GetX(5).OnOff);
break;
case "btnX6":
sim.SetX(6, !sim.GetX(6).OnOff);
break;
case "btnX7":
sim.SetX(7, !sim.GetX(7).OnOff);
break;
case "btnX10":
sim.SetX(10, !sim.GetX(10).OnOff);
break;
case "btnX11":
sim.SetX(11, !sim.GetX(11).OnOff);
break;
case "btnM0":
sim.SetM(0, !sim.GetM(0).OnOff);
break;
case "btnM1":
sim.SetM(1, !sim.GetM(1).OnOff);
break;
case "btnM2":
sim.SetM(2, !sim.GetM(2).OnOff);
break;
case "btnM3":
sim.SetM(3, !sim.GetM(3).OnOff);
break;
case "btnM4":
sim.SetM(4, !sim.GetM(4).OnOff);
break;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
sim.EndSimulation();
}
private void btnSave_Click(object sender, EventArgs e)
{
if (!sim.SimulationStatus && this.tbFileName.Text.Length>2 && this.richTextBox1.Text.Length>5)
{
bool fileExist = File.Exists(codePath + "\\" + this.tbFileName.Text.Trim());
string msg = "是否要保存" + this.tbFileName.Text.Trim() + "?";
if (fileExist) msg = "程序 [" + this.tbFileName.Text.Trim() + "] 已经存在,是否覆盖 ?";
var m1 = MessageBox.Show(msg, "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (m1 == DialogResult.OK)
{
try
{
File.WriteAllText(codePath + "\\" + this.tbFileName.Text.Trim(), richTextBox1.Text);
updateProgramList();
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
}
}
private void btnLoad_Click(object sender, EventArgs e)
{
if (!sim.SimulationStatus && lbxProList.SelectedIndex>=0)
{
var m1 = MessageBox.Show("是否要载入代码 " + lbxProList.SelectedItem.ToString() + " ?",
"提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (m1 == DialogResult.OK)
{
try
{
richTextBox1.Text = File.ReadAllText(codePath + "\\" + lbxProList.SelectedItem.ToString().Trim());
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
}
}
private void btnDelCodeFile_Click(object sender, EventArgs e)
{
if (!sim.SimulationStatus && lbxProList.SelectedIndex>=0)
{
var m1 = MessageBox.Show("是否要删除代码 " + lbxProList.SelectedItem.ToString() + " ?",
"提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
if (m1 == DialogResult.OK)
{
try
{
File.Delete(codePath + "\\" + lbxProList.SelectedItem.ToString().Trim());
this.tbFileName.Text = "";
this.richTextBox1.Text = "";
updateProgramList();
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
}
}
private void btnX0_MouseDown(object sender, MouseEventArgs e)
{
string name = ((Button)sender).Name;
switch (name)
{
case "btnX0":
sim.SetX(0, !sim.GetX(0).OnOff);
break;
case "btnX1":
sim.SetX(1, !sim.GetX(1).OnOff);
break;
case "btnX2":
sim.SetX(2, !sim.GetX(2).OnOff);
break;
case "btnX3":
sim.SetX(3, !sim.GetX(3).OnOff);
break;
case "btnX4":
sim.SetX(4, !sim.GetX(4).OnOff);
break;
}
}
private void btnX0_MouseUp(object sender, MouseEventArgs e)
{
string name = ((Button)sender).Name;
switch (name)
{
case "btnX0":
sim.SetX(0, !sim.GetX(0).OnOff);
break;
case "btnX1":
sim.SetX(1, !sim.GetX(1).OnOff);
break;
case "btnX2":
sim.SetX(2, !sim.GetX(2).OnOff);
break;
case "btnX3":
sim.SetX(3, !sim.GetX(3).OnOff);
break;
case "btnX4":
sim.SetX(4, !sim.GetX(4).OnOff);
break;
}
}
private void lbxProList_SelectedIndexChanged(object sender, EventArgs e)
{
var listbox = (ListBox)sender;
if(listbox.SelectedItem!=null)
tbFileName.Text = listbox.SelectedItem.ToString();
}
private void lbxProList_DoubleClick(object sender, EventArgs e)
{
btnLoad_Click(null, null);
}
private void tslStartSimulation_Click(object sender, EventArgs e)
{
if (this.richTextBox1.Lines.Length > 1)
{
try
{
sim.setCode(this.richTextBox1.Lines);
sim.StartSimulation();
this.tslStartSimulation.Enabled = false;
this.tslStopSimulation.Enabled = true;
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void tslStopSimulation_Click(object sender, EventArgs e)
{
if (!this.tslStartSimulation.Enabled)
{
sim.EndSimulation();
this.tslStartSimulation.Enabled = true;
this.tslStopSimulation.Enabled = false;
}
}
private void toolStripButton19_Click(object sender, EventArgs e)
{
//int startX = 30;
//Graphics g = this.pbImg.CreateGraphics();
//Pen p = new Pen(Color.Black, 1);
//g.DrawLine(p, new Point(startX, 0), new Point(startX, 5000));
//g.DrawLine(p, new Point(startX + 520, 0), new Point(startX + 520, 5000));
Scada formScada = new Scada();
formScada.ShowDialog();
}
void panMain_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DownPoint = new Point(e.X, e.Y);//保存鼠标按下坐标
uint rows = 1;
for (rows = 1; rows <= 16; rows++)
{
if (e.Y > (rows - 1) *panMain.cursorHeight && e.Y < rows *panMain.cursorHeight)
{
break;
}
}
ushort cols = 1;
for (cols = 1; cols <= 16; cols++)
{
if (e.X > (cols - 1) *panMain.cursorWidth && e.X < cols *panMain.cursorWidth &&
rows<=panMain.getHaveEditNums())
{
panMain.LadderCols = cols;
panMain.LadderRows = rows;
break;
}
}
panMain.Invalidate();
}
}
public void tsbLadderParamReset()
{
ladder.ladderParam = "";
ladder.ladderType = partTypeEnum.nopPart;
}
private void tsbNopen_Click(object sender, EventArgs e)
{
inputPart part=null;
switch (((ToolStripButton)sender).Name)
{
case "tsbNopen":
part= new inputPart(partTypeEnum.nopenPart);
break;
case "tsbOrOpen":
part = new inputPart(partTypeEnum.orOpenPart);
break;
case "tsbClose":
part = new inputPart(partTypeEnum.closePart);
break;
case "tsbOrClose":
part = new inputPart(partTypeEnum.orClosePart);
break;
case "tsbCoil":
part = new inputPart(partTypeEnum.coilPart);
break;
case "tsbApplication":
part = new inputPart(partTypeEnum.applicationPart);
break;
case "tsbHline":
part = new inputPart(partTypeEnum.hlinePart);
break;
case "tsbVline":
drawVline();
return;
case "tsbDelHline":
delHline();
return;
case "tsbDelVline":
delVline();
return;
case "tsbP":
part = new inputPart(partTypeEnum.pPart);
break;
case "tsbF":
part = new inputPart(partTypeEnum.fPart);
break;
case "tsbOrP":
part = new inputPart(partTypeEnum.orpPart);
break;
case "tsbOrF":
part = new inputPart(partTypeEnum.orfPart);
break;
case "tsbNot":
part = new inputPart(partTypeEnum.notPart);
break;
case "tsbAndLine":
break;
case "tsbDelAndLine":
break;
case "tsbNewLadder":
newLadder();
break;
case "tsbConvert":
ladderConverToCode();
return;
case "tsbDisCommon":
disOnOffCommon();
break;
case "tsbSaveLadder":
panMain.saveLadder();
return;
case "tsbOpenLadder":
panMain.readLadder();
panMain.Invalidate();
return;
default:
part = new inputPart(partTypeEnum.anyPart);
break;
}
var res = part.ShowDialog();
if (res == DialogResult.OK)
{
ladder.isHaveVline = panMain.getCurPosLadderNode().isHaveVline;
ladder.ladderParam = part.PartParam;
ladder.ladderType = part.PartType;
panMain.setProgramData(ladder);
panMain.Invalidate();
}
}
private void drawVline()
{
panMain.setCurPosLadderNodeVlineParam(true);
panMain.Invalidate();
}
private void delVline()
{
panMain.setCurPosLadderNodeVlineParam(false);
panMain.Invalidate();
}
private void delHline()
{
panMain.setProgramData(new LADDER()
{
ladderType = partTypeEnum.nopPart,
isHaveVline = panMain.getCurPosLadderNode().isHaveVline,
ladderParam = ""
});
panMain.Invalidate();
}
private void disOnOffCommon()
{
}
private void newLadder()
{
}
private void ladderConverToCode()
{
panMain.compileLadder();
richTextBox1.Lines= myPanel.complierCoder.ToArray();
}
private void drawPart(LADDER part)
{
ladder.isHaveVline = part.isHaveVline;
ladder.ladderParam = part.ladderParam;
ladder.ladderType = part.ladderType;
panMain.setProgramData(ladder);
panMain.Invalidate();
}
}
}关于这个仿真器,勇哥还写了一篇论文,有兴趣的朋友可以参考:
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!


少有人走的路


















