# 西门子PLC的详细的数据访问功能 #### 配置西门子PLC的以太网模块 环境:此处使用了STEP 7V5.5 sp4编程软件作为示例,在添加以太网模块(6GK7 343-1EX30-0E0 CP343-1)到组态中时,可以设置IP地址及子网掩码, 此处测试使用,所以不使用路由器,如果您的西门子需要连接到内网中的话,需要配置路由器。目前只支持M,I,Q数据的读写。 然后点击新建,创建一个Ethernet(1)网络。以太网参数配置如下图: ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_1.jpg) 将以太网的模块添加到机架中以后,现在打开网络组态 ,打开后点击组态上的PLC模块。会出现如下界面,在箭头出进行双击操作,可以弹出对话框,并进行一系列操作: ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_2.jpg) ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_3.jpg) ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_4.jpg) ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_5.jpg) ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_6.jpg) 按照上面一套操作下来,创建了一个读取的端口,端口号为2000,后面有用,需要记住, 按照上述的步骤再创建一个写入的端口,只有最后一步不一致,如下: ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_7.jpg) 配置完之后的效果图如下,新建了两个端口,一个用于读取数据,一个用于写入数据。 注意:设置完成后一定要写入到PLC才算真的完成。 ![](https://github.com/dathlin/C-S-/raw/master/img/plc_siemens_8.jpg) 如上图所示,共配置了2000,2001两个端口号,配置完成后需要进行重启PLC,端口的配置原则如下: 端口号设置规则: 西门子PLC的数据种类其实只有一种,就是byte数据,更大的数据用多个byte来组合,位数据就是byte上的位,所以会比三菱的简单一点点,好处理一点。 #### 初始化访问PLC对象 如果想使用本组件的数据读取功能,必须先初始化数据访问对象,根据实际情况进行数据的填入。 下面仅仅是测试中的数据:

	private SiemensNet siemens_net = new SiemensNet();
	private void FormPlcTest_Load(object sender, EventArgs e)
	{
		//初始化,此处的值参考了上面的配置参数
		siemens_net.ConnectTimeout = 500;
		siemens_net.PortRead = 2000;
		siemens_net.PortReadBackup = 2002;//备用读端口,也可以不指定,默认负数,不会切换负数端口
		siemens_net.PortWrite = 2001;
		siemens_net.PLCIpAddress = System.Net.IPAddress.Parse("192.168.0.6");
	}

说明:对象应该放在窗体类下面,此处仅仅针对读取一台设备的plc,也可以在访问的方法中实例化局部对象, 初始化数据,然后读取,该对象几乎不损耗内存,内存垃圾由CLR进行自动回收。此处测试方便,窗体的多个按钮均连接同一台PLC 设备,所以本窗体实例化一个对象即可。 #### M,I,Q数据读写 由于西门子这三个数据种类相同,操作时一模一样,所以此处只展示读写M寄存器的例子。 如下方法演示读取了M100-M101这2个M的值,返回值解析:如果读取正常则共返回2个字节的数据,以下示例数据

        private void button3_Click(object sender, EventArgs e)
        {
            OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 2);
            if(read.IsSuccess)
            {
                textBox4.Text = "M100:" + read.Content[0] + " M101:" + read.Content[1];
                bool M100_0 = (read.Content[0] & 0x01) == 0x01;//M100.0的通断
                bool M100_1 = (read.Content[0] & 0x02) == 0x02;//M100.1的通断
                bool M100_2 = (read.Content[0] & 0x04) == 0x04;//M100.2的通断
                bool M100_3 = (read.Content[0] & 0x08) == 0x08;//M100.3的通断
                bool M100_4 = (read.Content[0] & 0x10) == 0x10;//M100.4的通断
                bool M100_5 = (read.Content[0] & 0x20) == 0x20;//M100.5的通断
                bool M100_6 = (read.Content[0] & 0x40) == 0x40;//M100.6的通断
                bool M100_7 = (read.Content[0] & 0x80) == 0x80;//M100.7的通断
            }
            else
            {
                MessageBox.Show(read.ToMessageShowString());
            }
        }
        private void button4_Click(object sender, EventArgs e)
        {
            //写入结果是M100:81  M101:22  M102:124
            OperateResultBytes write = siemens_net.WriteIntoPLC(SiemensDataType.M, 100, new byte[] { 81, 22, 124 });
            if (write.IsSuccess)
            {
                textBox4.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

错误说明:有可能因为站号网络号没有配置正确返回有错误代号没有错误信息, 也有可能因为网络问题导致没有连接上,此时会有连接不上的错误信息。 下面展示的是后台线程循环读取的情况,事实上在实际的使用过程中经常会碰见的情况。下面的方法需要 放到单独的线程中,同理,访问D数据时也是按照下面循环就行,此处不再赘述。

	//后台循环读取PLC数据 M200开始10个字 也即是M200-M209
	while (true)
	{
		OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 2);
		if (read.IsSuccess)
		{
			//成功读取,委托显示
			textBox2.BeginInvoke(new Action(delegate
			{
				textBox4.Text = "M100:" + read.Content[0] + " M101:" + read.Content[1];
			}));
		}
		else
		{
			//失败读取,应该对失败信息进行日志记录,不应该显示,测试访问时才适合显示错误信息
			LogHelper.save(read.ToMessageShowString());
		}
		System.Threading.Thread.Sleep(1000);//决定了访问的频率
	}

#### 整数数据读写(一个数据由2个byte组成) 虽然上述实现了M数据的读写,但是只能表示0-255的数据,想要支持更大的数据,需要自己指定规则, 这就需要你对数据和字节原理非常清晰才能实现,为了方便,此处提供了读写双字节数据的功能,先演示读取M100-M106 的数据,对应有三个双字节数据,代码如下:

	private void button15_Click(object sender, EventArgs e)
	{
		//整数读取
		OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 6);
		if (read.IsSuccess)
		{
			short[] result = siemens_net.GetArrayFromBytes(read.Content);
			textBox4.Text = "M100:" + result[0] + ",M102:" + result[1] + ",M104:" + result[2];
			//或者下述的方法或许,可以发现和三菱的D数据获取是一模一样的
			//short m100 = BitConverter.ToInt16(read.Content, 0);
			//short m102 = BitConverter.ToInt16(read.Content, 2);
			//short m104 = BitConverter.ToInt16(read.Content, 4);
		}
		else
		{
			MessageBox.Show(read.ToMessageShowString());
		}
	}

实际使用中,如果有不明白的地方,可以咨询我,接下来介绍写入整数数据的操作,例如,我们要使得M100,M101=2456,M102,M103=4567,M104,M105=-124,那么代码如下:

	private void button16_Click(object sender, EventArgs e)
	{
		//整数写入,数组的长度为x,那么占用的M字节数为x乘以2
		OperateResultBytes write = siemens_net.WriteIntoPLC(SiemensDataType.M, 100, new short[] { 2456, 4567, -124 });
		if (write.IsSuccess)
		{
			textBox4.Text = "写入成功";
		}
		else
		{
			MessageBox.Show(write.ToMessageShowString());
		}
	}

#### ASCII字符串数据的读写 在实际项目中,有可能会碰到PLC存储了规格数据,或是条码数据,这些数据是以ASCII编码形式存在, 我们需要把数据进行读取出来用于显示,保存等操作。下面演示读取指定长度的条码数据,数据的数据存放在M100-M109中, 长度应该为存储条码的最大长度,也即是占用了10个M,一个M可以存储1个ASCII码字符:

	private void button12_Click(object sender, EventArgs e)
	{
		//读取字符串,长度为10
		OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 100, 10);
		if (read.IsSuccess)
		{
			textBox4.Text = Encoding.ASCII.GetString(read.Content);
		}
		else
		{
			MessageBox.Show(read.ToMessageShowString());
		}
	}

下面演示写入条码数据,地址在M100-M109中,所以需要写入10个字符:

	private void button14_Click(object sender, EventArgs e)
	{
		//写字符串,字符串的长度决定了需要占用多少个M字节,两者是相等的
		OperateResultBytes write = siemens_net.WriteAsciiStringIntoPLC(SiemensDataType.M, 100, "K123456789");
		if (write.IsSuccess)
		{
			textBox4.Text = "写入成功";
		}
		else
		{
			MessageBox.Show(write.ToMessageShowString());
		}
	}

需要注意的是,如果第一次在M100-M109中写入了"K123456789",第二次写入了"K6666",那么读取M100-M109的条码数据会读取到K666656789,如果要避免这种情况,则需要在写入条码的时候,指定总长度,该长度 可单数可偶数,具体的使用方法如下:

	private void button14_Click(object sender, EventArgs e)
	{
		//写字符串
		OperateResultBytes write = siemens_net.WriteAsciiStringIntoPLC(SiemensDataType.M, 100, "K6666", 10);
		if (write.IsSuccess)
		{
			textBox4.Text = "写入成功";
		}
		else
		{
			MessageBox.Show(write.ToMessageShowString());
		}
	}

#### 中文及特殊字符的读写 在需要读写复杂的字符数据时,上述的ASCII编码已经不能满足要求,虽然使用读写的基础方法可以实现任意数据的读写, 但是此处为了方便,还是提供了一个方便的方法来读写中文数据,采用Unicode编码的字符, 该编码下的一个字符占用两个M来存储。如下将演示,读写方法,基本用途和上述 ASCII编码的读写一致。

	private void button11_Click(object sender, EventArgs e)
	{
		//读取中文 指定读取的长度必须双数,否则解码失败
		OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 200, 12);
		if (read.IsSuccess)
		{
			textBox4.Text = Encoding.Unicode.GetString(read.Content);
		}
		else
		{
			MessageBox.Show(read.ToMessageShowString());
		}
	}

在写入的过程中,只演示写入指定长度的(实际中也应该使用这个方法),指定长度的意思为多少个中文。

	private void button13_Click(object sender, EventArgs e)
	{
		//中文写入
		OperateResultBytes write = siemens_net.WriteUnicodeStringIntoPLC(SiemensDataType.M, 200, "测试de东西", 10);
		if (write.IsSuccess)
		{
			textBox4.Text = "写入成功";
		}
		else
		{
			MessageBox.Show(write.ToMessageShowString());
		}
	}

#### 一个实际中复杂的例子演示 实际中可能碰到的情况会很复杂,一台设备中需要上传的数据包含了温度,压力,产量,规格等等信息,在一串数据中 会包含各种各样的不同的数据,所以此处做一个完整示例的演示,假设我们需要读取 M100-M116的数据,假设M100,M101存放了温度数据,55.1℃在M中为551,M102,M103存放了压力数据,1.23MPa在M中存放为123,M104存放了 设备状态,0为停止,1为运行,M105,M106存放了产量,1000就是指1000个,M107-M116存放了规格,以下代码演示如何去解析数据:

	private void button29_Click(object sender, EventArgs e)
	{
		//解析复杂数据
		OperateResultBytes read = siemens_net.ReadFromPLC(SiemensDataType.M, 200, 17);
		if (read.IsSuccess)
		{
			double 温度 = BitConverter.ToInt16(read.Content, 0) / 10d;//索引很重要
			double 压力 = BitConverter.ToInt16(read.Content, 2) / 100d;
			bool IsRun = read.Content[4] == 1;
			int 产量 = BitConverter.ToInt16(read.Content, 5);
			string 规格 = Encoding.ASCII.GetString(read.Content, 7, 10);
		}
		else
		{
			MessageBox.Show(read.ToMessageShowString());
		}
	}

#### 高并发时的高性能访问方法 还未完成,等待发布。