命名空间: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
版权声明:本文为博主原创文章,转载请附上博文链接!

