本文將使用一個Github開源的組件庫技術來讀寫三菱PLC和西門子plc數據,使用的是基於以太網的TCP/IP實現,不需要額外的組件,讀取操作只要放到后台線程就不會卡死線程,本組件支持超級方便的高性能讀寫操作
github地址:https://github.com/dathlin/HslCommunication 如果喜歡可以star或是fork,還可以打賞支持,打賞請認准源代碼項目。
在Visual Studio 中的NuGet管理器中可以下載安裝,也可以直接在NuGet控制台輸入下面的指令安裝:
1
|
Install-Package HslCommunication
|
如果需要教程:Nuget安裝教程:http://www.cnblogs.com/dathlin/p/7705014.html
聯系作者及加群方式(激活碼在群里發放):http://www.hslcommunication.cn/Cooperation
組件的完整信息和API介紹參照:http://www.cnblogs.com/dathlin/p/7703805.html 組件的使用限制,更新日志,都在該頁面里面。
如果你需要在讀取PLC數據之后,還要群發客戶端來實現遠程辦公室同步監視,可以參考如下的項目(基於該組件擴展起來的,帶有賬戶驗證,版本控制,數據群發,公告管理等等功能)
https://github.com/dathlin/ClientServerProject
本文將展示如何配置網絡參數及怎樣使用代碼來訪問PLC數據,希望給有需要的人解決一些實際問題。主要對三菱Q系列PLC的X,Y,M,L,B,V,F,S,D,W,R區域的數據讀寫,對西門子PLC的M,Q,I,DB塊的數據讀寫,親測有效。
此處使用了網線直接的方式,如果PLC接進了局域網,就可以進行遠程讀寫了^_^
此處使用到了2個命名空間:
1
2
|
using
HslCommunication;
using
HslCommunication.Profinet;
|
隨便聊聊
當我們一個上位機需要讀取100台西門子PLC設備(此處只是舉個例子,凡是都是使用Modbus tcp的都是一樣的)的時候,你采用服務器主動去請求100台設備的機制對性能來說是個極大的考驗,如果開100個線程去輪詢100台設備,那么性能損失將是非常大的,更不用說再增加設備,如果搭建Modbus tcp服務器,就可以完美的解決性能問題,因為連接的壓力將會平均分攤給每一台PLC,服務器端只要新增一個時間戳就可以知道客戶端有沒有連接上。
我們在100台PLC里都增加發送Modbus tcp方法,將數據發送到服務器的ip和端口上去,服務器根據站號來區分設備。這樣就可以搭建一個高性能總站。 本組件支持快速搭建一個高性能的Modbus tcp總站。
http://www.cnblogs.com/dathlin/p/7782315.html
關於兩種模式
在PLC端,包括三菱,西門子,歐姆龍以及Modbus Tcp客戶端的訪問器上,都支持兩種模式,短連接模式和長連接模式,現在就來解釋下什么原理。
短連接:每次讀寫都是一個單獨的請求,請求完畢也就關閉了,如果服務器的端口僅僅支持單連接,那么關閉后這個端口可以被其他連接復用,但是在頻繁的網絡請求下,容易發生異常,會有其他的請求不成功,尤其是多線程的情況下。
長連接:創建一個公用的連接通道,所有的讀寫請求都利用這個通道來完成,這樣的話,讀寫性能更快速,即時多線程調用也不會影響,內部有同步機制。如果服務器的端口僅僅支持單連接,那么這個端口就被占用了,比如三菱的端口機制,西門子的Modbus tcp端口機制也是這樣的。以下代碼默認使用長連接,性能更高,還支持多線程同步。
在短連接的模式下,每次請求都是單獨的訪問,所以沒有重連的困擾,在長連接的模式下,如果本次請求失敗了,在下次請求的時候,會自動重新連接服務器,直到請求成功為止。另外,盡量所有的讀寫都對結果的成功進行判斷。
關於日志記錄
不管是三菱的數據訪問類,還是西門子的,還是Modbus tcp訪問類,都有一個LogNet屬性用來記錄日志,該屬性是一個接口類,ILogNet,凡事繼承該接口的都可以用來記錄日志,該日志會在訪問失敗時,尤其是因為網絡的原因導致訪問失敗時會進行日志記錄(如果你為這個 LogNet 屬性配置了真實的日志記錄器的話):如果你想使用該記錄日志的功能,請參照如下的博客進行實例化:
http://www.cnblogs.com/dathlin/p/7691693.html
訪問測試項目
下面的一個項目是這個組件的訪問測試項目,您可以進行初步的訪問的測試,免去了您寫測試程序的麻煩,三菱的界面和西門子的界面幾乎是一致的。可以同時參考。該項目位於本篇文章開始處的Gitbub源代碼里面的
下載地址為:HslCommunicationDemo.zip
演示項目
下面的三篇演示了具體如何去訪問PLC的數據,我們在訪問完成后,通常需要進行處理,以下的示例項目就演示了后台從PLC讀取數據后,前台顯示並推送給所有在線客戶端的功能,客戶端並進行圖形化顯示,具有一定的參考意義,並且推送給網頁前端,項目地址為:
https://github.com/dathlin/RemoteMonitor
下面的圖片示例中的左邊程序就是服務器程序,它應該和PLC直接連接並接入局域網,然后把數據推送給客戶端顯示。注意:一個復雜高級的程序就應該把處理邏輯程序和界面程序分開,比如這里的服務器程序實現數據采集,推送,存儲。讓客戶端程序去實現數據的整理,分析,顯示,這樣即使客戶端程序因為BUG奔潰,服務器端仍然可以正常的工作。
三菱PLC篇(下面列舉了三種配置方法,本組件支持二進制和ASCII通訊,支持1E幀兼容協議訪問)
Q06UDV Plc的訪問測試感謝:hwdq0012
fx5u plc的訪問測試感謝:山楂
Q02CPU, L02CPU-CM : 本人測試
感謝:小懶豬雨中人 的測試,VB程序也可以調用本通訊庫
環境1:此處以GX Works3為示例,fx5u的配置如下:(感謝 山楂 提供的圖片)
環境2:此處以GX Works2為示例,測試PLC為L02CPU,內置了以太網協議
環境3:此處以GX Works2為示例,添加以太網模塊,型號為QJ71E71-100,組態里添加完成后進行以太網的參數配置,此處需要注意的是:參數的配置對接下來的代碼中配置參數要一一對應
注意:在PLC的以太網模塊的配置中,無法設置網絡號為0,也無法設置站號為0, 所以此處均設置為1,在C#程序中也使用上述的配置,在代碼中均配置為0,如果您自定義設置為網絡2, 站號8,那么在代碼中就要寫對應的數據。如果仍然通信失敗,重新測試0,0。
打開設置:在上圖中的打開設置選項,進行其他參數的配置,下圖只是舉了一個例子,開通了4個端口來支持讀寫操作:
端口號設置規則:
- 為了不與原先存在的系統發生沖突,您在添加自己的端口時盡量使用您自己的端口。
- 如果讀寫都需要,盡可能的將讀取端口和寫入端口區分開來,這樣做比較高性能。
- 如果您的網絡狀態不是特別穩定,讀取端口使用2個,一個受阻切換另一個讀取可以提升系統的穩定性。
本文檔僅作組件的測試,所以只用了一個端口作為讀寫。如果你的程序也使用了一個端口,那么你在讀取數據時候, 剛好也在寫入(異步操作可能發生這樣的情況),那么寫入會失敗!)(在長連接模式下沒有這個問題)
三菱PLC的數據主要由兩類數據組成,位數據和字數據,在位數據中,例如X,Y,M,L都是位數據,字數據例如D,W。 兩類的數據在讀取解碼上存在一點小差別。(事實上也可以先將16個M先賦值給一個D,讀取D數據再進行解析, 在讀取M的數量比較多的時候,這樣操作效率更高)
初始化訪問PLC對象
注意:如果你想采用ASCII來讀寫數據,請使用MelsecMcAsciiNet類,如果想采用1E幀協議,使用MelsecA1ENet類,除了實例化,其他的數據交互都是一樣的。
如果想使用本組件的數據讀取功能,必須先初始化數據訪問對象,根據實際情況進行數據的填入。 下面僅僅是測試中的數據:
1
|
private
MelsecMcNet melsec_net =
new
MelsecMcNet(
"192.168.0.1"
, 6000 );
|
如上圖所示,只要指定了IP地址和端口號就完成了初始化的搭建了,當然還支持一些額外的信息配置
1
2
3
|
melsec_net.ConnectTimeOut = 2000;
// 網絡連接的超時時間
melsec_net.NetworkNumber = 0x00;
// 網絡號
melsec_net.NetworkStationNumber = 0x00;
// 網絡站號
|
打開連接,並可以判斷是否連接上
1
|
melsec_net.ConnectClose( );
|
如果需要判斷,那么按照如下的操作
1
2
3
4
5
6
7
8
9
|
OperateResult connect = melsec_net.ConnectServer( );
if
(connect.IsSuccess)
{
MessageBox.Show(
"連接成功!"
);
}
else
{
MessageBox.Show(
"連接失敗!"
);
}
|
說明:對象應該放在窗體類下面,此處僅僅針對讀取一台設備的plc,也可以在訪問的方法中實例化局部對象, 初始化數據,然后讀取,該對象幾乎不損耗內存,內存垃圾由CLR進行自動回收。此處測試方便,窗體的多個按鈕均連接同一台PLC 設備,所以本窗體實例化一個對象即可。
關於兩種地址的表示方式
第一種,使用系統的類來標識,比如M200,寫成(MelsecDataType.M, 200)的表示形式,這樣也可以去MelsecDataType里面找到所有支持的數據類型。
第二種,使用字符串表示,這個組件里所有的讀寫操作提供字符串表示的重載方法,所有的支持訪問的類型對應如下,字符串的表示方式存在十進制和十六進制的區別:
- 輸入繼電器:"X100","X1A0" // 字符串為十六進制機制
- 輸出繼電器:"Y100" ,"Y1A0" // 字符串為十六進制機制
- 內部繼電器:"M100","M200" // 字符串為十進制
- 鎖存繼電器:"L100" ,"L200" // 字符串為十進制
- 報警器: "F100", "F200" // 字符串為十進制
- 邊沿繼電器:"V100" , "V200" // 字符串為十進制
- 鏈接繼電器:"B100" , "B1A0" // 字符串為十六進制
- 步進繼電器:"S100" , "S200" // 字符串為十進制
- 數據寄存器:"D100", "D200" // 字符串為十進制
- 鏈接寄存器:"W100" ,"W1A0" // 字符串為十六進制
- 文件寄存器:"R100","R200" // 字符串為十進制
展示一些簡單實用基礎數據讀寫,這些數據的讀寫沒有進行嚴格的是否成功判斷(判斷方法參照后面的代碼),一般網絡良好的情況下都會成功,但不排除失敗,以下代碼僅作測試,所有沒有嚴格判斷是否成功:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
bool
[] M100 = melsec_net.ReadBool(
"M100"
,1).Content;
// 讀取M100是否通,十進制地址
bool
[] X1A0 = melsec_net.ReadBool(
"X1A0"
,1).Content;
// 讀取X1A0是否通,十六進制地址
bool
[] Y1A0 = melsec_net.ReadBool(
"Y1A0"
,1).Content;
// 讀取Y1A0是否通,十六進制地址
bool
[] B1A0 = melsec_net.ReadBool(
"B1A0"
,1).Content;
// 讀取B1A0是否通,十六進制地址
short
short_D1000 = melsec_net.ReadInt16(
"D1000"
).Content;
// 讀取D1000的short值 ,W3C0,R3C0 效果是一樣的
ushort
ushort_D1000 = melsec_net.ReadUInt16(
"D1000"
).Content;
// 讀取D1000的ushort值
int
int_D1000 = melsec_net.ReadInt32(
"D1000"
).Content;
// 讀取D1000-D1001組成的int數據
uint
uint_D1000 = melsec_net.ReadUInt32(
"D1000"
).Content;
// 讀取D1000-D1001組成的uint數據
float
float_D1000 = melsec_net.ReadFloat(
"D1000"
).Content;
// 讀取D1000-D1001組成的float數據
long
long_D1000 = melsec_net.ReadInt64(
"D1000"
).Content;
// 讀取D1000-D1003組成的long數據
ulong
ulong_D1000 = melsec_net.ReadUInt64(
"D1000"
).Content;
// 讀取D1000-D1003組成的long數據
double
double_D1000 = melsec_net.ReadDouble(
"D1000"
).Content;
// 讀取D1000-D1003組成的double數據
string
str_D1000 = melsec_net.ReadString(
"D1000"
, 10).Content;
// 讀取D1000-D1009組成的條碼數據
melsec_net.Write(
"M100"
,
new
bool
[] {
true
} );
// 寫入M100為通
melsec_net.Write(
"Y1A0"
,
new
bool
[] {
true
} );
// 寫入Y1A0為通
melsec_net.Write(
"X1A0"
,
new
bool
[] {
true
} );
// 寫入X1A0為通
melsec_net.Write(
"B1A0"
,
new
bool
[] {
true
} );
// 寫入B1A0為通
melsec_net.Write(
"D1000"
, (
short
)1234);
// 寫入D1000 short值 ,W3C0,R3C0 效果是一樣的
melsec_net.Write(
"D1000"
, (
ushort
)45678);
// 寫入D1000 ushort值
melsec_net.Write(
"D1000"
, 1234566);
// 寫入D1000 int值
melsec_net.Write(
"D1000"
, (
uint
)1234566);
// 寫入D1000 uint值
melsec_net.Write(
"D1000"
, 123.456f);
// 寫入D1000 float值
melsec_net.Write(
"D1000"
, 123.456d);
// 寫入D1000 double值
melsec_net.Write(
"D1000"
, 123456661235123534L);
// 寫入D1000 long值
melsec_net.Write(
"D1000"
,
"K123456789"
);
// 寫入D1000 string值
|
下面再分別講解嚴格的操作,以及批量化的復雜的讀寫操作,假設你要讀取1000個M,循環讀取1千次可能要3秒鍾,如果用了下面的批量化讀取,只需要50ms,但是需要你對字節的原理比較熟悉才能得心應手的處理
X,Y,M,L,F,V,B,S位數據的讀寫說明
- X 輸入繼電器
- Y 輸出繼電器
- M 內部繼電器
- L 鎖存繼電器
- F 報警器
- V 邊沿繼電器
- B 鏈接繼電器
- S 步進繼電器
本小節將展示八種位數據的讀取,雖然更多的時候只是讀取D數據即可,或者是將位數據批量挪到D數據中, 但是在此處仍然進行介紹單獨的讀取X,Y,M,L,F,V,B,S,由於這八種讀取手法一致,故針對M數據進行介紹,其他的您可以自己測試。
如下方法演示讀取了M200-M209這10個M的值,注意:讀取長度必須為偶數,即時寫了奇數,也會補齊至偶數,讀取和寫入的最大長度為7168,否則報錯。如需實際需求確實大於7168的,請分批次讀取。
返回值解析:如果讀取正常則共返回10個字節的數據,以下示例數據進行批量化的讀取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
private
void
userButton20_Click(
object
sender, EventArgs e)
{
// M200-M209讀取顯示
OperateResult<
bool
[]> read = melsec_net.ReadBool(
"M200"
, 10);
if
(read.IsSuccess)
{
// 成功讀取,True代表通,False代表不通
bool
M200 = read.Content[0];
bool
M201 = read.Content[1];
bool
M202 = read.Content[2];
bool
M203 = read.Content[3];
bool
M204 = read.Content[4];
bool
M205 = read.Content[5];
bool
M206 = read.Content[6];
bool
M207 = read.Content[7];
bool
M208 = read.Content[8];
bool
M209 = read.Content[9];
// 顯示
}
else
{
//失敗讀取,顯示失敗信息
MessageBox.Show(read.ToMessageShowString());
}
}
private
void
userButton21_Click(
object
sender, EventArgs e)
{
// X100-X10F讀取顯示
OperateResult<
bool
[]> read = melsec_net.ReadBool(
"X200"
, 16);
if
(read.IsSuccess)
{
// 成功讀取,True代表通,False代表不通
bool
X200 = read.Content[0];
bool
X201 = read.Content[1];
bool
X202 = read.Content[2];
bool
X203 = read.Content[3];
bool
X204 = read.Content[4];
bool
X205 = read.Content[5];
bool
X206 = read.Content[6];
bool
X207 = read.Content[7];
bool
X208 = read.Content[8];
bool
X209 = read.Content[9];
bool
X20A = read.Content[10];
bool
X20B = read.Content[11];
bool
X20C = read.Content[12];
bool
X20D = read.Content[13];
bool
X20E = read.Content[14];
bool
X20F = read.Content[15];
// 顯示
}
else
{
//失敗讀取,顯示失敗信息
MessageBox.Show(read.ToMessageShowString());
}
}
private
void
userButton3_Click(
object
sender, EventArgs e)
{
// M100-M104 寫入測試 此處寫入后M100:通 M101:斷 M102:斷 M103:通 M104:通
bool
[] values =
new
bool
[] {
true
,
false
,
false
,
true
,
true
};
// 等同於 byte[] values = new byte[]{0x01,0x00,0x00,0x01,0x01}
OperateResult write = melsec_net.Write(
"M100"
, values);
if
(write.IsSuccess)
{
TextBoxAppendStringLine(
"寫入成功"
);
}
else
{
MessageBox.Show(write.ToMessageShowString());
}
}
|
錯誤說明:有可能因為站號網絡號沒有配置正確返回有錯誤代號沒有錯誤信息, 也有可能因為網絡問題導致沒有連接上,此時會有連接不上的錯誤信息。
下面展示的是后台線程循環讀取的情況,事實上在實際的使用過程中經常會碰見的情況。下面的方法需要 放到單獨的線程中,同理,訪問D數據時也是按照下面循環就行,此處不再贅述。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//后台循環讀取PLC數據 M200開始10個字 也即是M200-M209
while
(
true
)
{
OperateResult<
bool
[]> read = melsec_net.ReadFromPLC(
"M200"
, 10);
if
(read.IsSuccess)
{
//成功讀取,委托顯示
textBox2.BeginInvoke(
new
Action(
delegate
{
textBox2.Text =
"M201:"
+ (read.Content[1] ?
"通"
:
"斷"
);
}));
}
else
{
//失敗讀取,應該對失敗信息進行日志記錄,不應該顯示,測試訪問時才適合顯示錯誤信息
LogHelper.save(read.ToMessageShowString());
}
System.Threading.Thread.Sleep(1000);
//決定了訪問的頻率
}
|
D,W,R字數據的讀寫操作
此處讀取針對中間存在整數數據的情況,因為兩者讀取方式相同,故而只演示一種數據讀取, 使用該組件讀取數據,一次最多讀取或寫入960個字,超出則失敗。 如果讀取的長度確實超過限制,請考慮分批讀取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
private
void
userButton2_Click(
object
sender, EventArgs e)
{
// D100-D104讀取
OperateResult<
byte
[]> read = melsec_net.Read(
"D100"
, 5);
if
(read.IsSuccess)
{
// 成功讀取,提取各自的值,此處的值有個前提假設,假設PLC上的數據是有符號的數據,表示-32768-32767
short
D100 = melsec_net.ByteTransform.TransInt16(read.Content, 0);
short
D101 = melsec_net.ByteTransform.TransInt16( read.Content, 2);
short
D102 = melsec_net.ByteTransform.TransInt16( read.Content, 4);
short
D103 = melsec_net.ByteTransform.TransInt16( read.Content, 6);
short
D104 = melsec_net.ByteTransform.TransInt16( read.Content, 8);
TextBoxAppendStringLine(
"D100:"
+ D100);
TextBoxAppendStringLine(
"D101:"
+ D101);
TextBoxAppendStringLine(
"D102:"
+ D102);
TextBoxAppendStringLine(
"D103:"
+ D103);
TextBoxAppendStringLine(
"D104:"
+ D104);
}
else
{
//失敗讀取
MessageBox.Show(read.ToMessageShowString());
}
}
private
void
userButton4_Click(
object
sender, EventArgs e )
{
short
[] values =
new
short
[5] { 1335, 8765, 1234, 4567, -2563 };
// D100為1234,D101為8765,D102為1234,D103為4567,D104為-2563
OperateResult write = melsec_net.Write(
"D6000"
, values );
if
(write.IsSuccess)
{
//成功寫入
TextBoxAppendStringLine(
"寫入成功"
);
}
else
{
MessageBox.Show( write.ToMessageShowString( ) );
}
}
|
ASCII字符串數據的讀寫
在實際項目中,有可能會碰到PLC存儲了規格數據,或是條碼數據,這些數據是以ASCII編碼形式存在, 我們需要把數據進行讀取出來用於顯示,保存等操作。下面演示讀取指定長度的條碼數據,數據的數據存放在D2000-D2004中, 長度應該為存儲條碼的最大長度,也即是占用了5個D,一個D可以存儲2個ASCII碼字符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
private
void
button7_Click(
object
sender, EventArgs e)
{
//讀取字符串數據,共計10個字節長度
OperateResult<
byte
[]> read = melsec_net.Read(
"D2000"
, 5);
if
(read.IsSuccess)
{
//成功讀取
textBox2.Text = Encoding.ASCII.GetString(read.Content);
}
else
{
//失敗讀取
MessageBox.Show(read.ToMessageShowString());
}
}
private
void
button8_Click(
object
sender, EventArgs e)
{
//寫字符串,如果寫入K12345678這9個字符,讀取出來時末尾會補0
OperateResult write = melsec_net.WriteAsciiString(
"D2000"
,
"K123456789"
);
if
(write.IsSuccess)
{
textBox2.Text =
"寫入成功"
;
}
else
{
MessageBox.Show(write.ToMessageShowString());
}
}
|
需要注意的是,如果第一次在D2000-D2004中寫入了"K123456789",第二次寫入了"K6666",那么讀取D2000-D2004的條碼數據會讀取到 K666656789,如果要避免這種情況,則需要在寫入條碼的時候,指定總長度,該長度必須為偶數, 不然也會自動補0,小於該長度時,自動補零,大於該長度時,自動截斷數據,具體的使用方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private
void
button8_Click(
object
sender, EventArgs e)
{
//寫字符串,本次寫入指定了10個長度的字符,其余的D的數據將被清空,是一種安全的寫入方式
OperateResult write = melsec_net.WriteAsciiString(
"D2000"
,
"K6666"
, 10);
if
(write.IsSuccess)
{
textBox2.Text =
"寫入成功"
;
}
else
{
MessageBox.Show(write.ToMessageShowString());
}
}
|
中文及特殊字符的讀寫
在需要讀寫復雜的字符數據時,上述的ASCII編碼已經不能滿足要求,雖然使用讀寫的基礎方法可以實現任意數據的讀寫, 但是此處為了方便,還是提供了一個方便的方法來讀寫中文數據,采用Unicode編碼的字符, 該編碼下的一個字符占用一個D或W來存儲。如下將演示,讀寫方法,基本用途和上述 ASCII編碼的讀寫一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
private
void
button9_Click(
object
sender, EventArgs e)
{
//讀中文,存儲在D3000-D3009
OperateResult<
byte
[]> read = melsec_net.Read(
"D3000"
, 10);
if
(read.IsSuccess)
{
//解析數據
textBox2.Text = Encoding.Unicode.GetString(read.Content);
}
else
{
MessageBox.Show(read.ToMessageShowString());
}
}
private
void
button10_Click(
object
sender, EventArgs e)
{
//寫中文 D3000-D3009,該10含義為中文字符數
OperateResult write = melsec_net.WriteUnicodeString(
"D3000"
,
"測試數據test"
, 10);
if
(write.IsSuccess)
{
textBox2.Text =
"寫入成功"
;
}
else
{
MessageBox.Show(write.ToMessageShowString());
}
}
|
一個實際中復雜的例子演示
實際中可能碰到的情況會很復雜,一台設備中需要上傳的數據包含了溫度,壓力,產量,規格等等信息,在一串數據中 會包含各種各樣的不同的數據,上述的讀取D,讀取M,讀取條碼的方式不太好用,所以此處做一個完整示例的演示,假設我們需要讀取 D4000-D4009的數據,假設D4000存放了溫度數據,55.1℃在D中為551,D4001存放了壓力數據,1.23MPa在D中存放為123,D4002存放了 設備狀態,0為停止,1為運行,D4003存放了產量,1000就是指1000個,D4004備用,D4005-D4009存放了規格,以下代碼演示如何去解析數據:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
private
void
button29_Click(
object
sender, EventArgs e)
{
//解析復雜數據
OperateResult<
byte
[]> read = melsec_net.Read(
"D4000"
, 10);
if
(read.IsSuccess)
{
double
溫度 = melsec_net.ByteTransform.TransInt16(read.Content, 0) / 10d;
//索引很重要
double
壓力 = melsec_net.ByteTransform.TransInt16(read.Content, 2) / 100d;
bool
IsRun = melsec_net.ByteTransform.TransInt16(read.Content, 4) == 1;
int
產量 = BitConverter.ToInt16(read.Content, 6);
string
規格 = Encoding.ASCII.GetString(read.Content, 10, 10);
}
else
{
MessageBox.Show(read.ToMessageShowString());
}
}
|
究極數據讀取展示,用於測試你自己的報文以及擴展自己的更高級,更變態的API,以下演示,使用這個高級模式,寫入M100,True的操作:
我們要寫入的字節數組HEX表示形式為:50 00 00 FF FF 03 00 0D 00 0A 00 01 14 01 00 64 00 00 90 01 00 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private
void
userButton23_Click(
object
sender, EventArgs e)
{
byte
[] buffer = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes(
"50 00 00 FF FF 03 00 0D 00 0A 00 01 14 01 00 64 00 00 90 01 00 10"
);
// 直接使用報文進行
OperateResult<
byte
[]> operate = melsec_net.ReadFromCoreServer(buffer);
if
(operate.IsSuccess)
{
// 返回PLC的報文反饋,需要自己對報文進行結果分析
MessageBox.Show(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));
}
else
{
// 網絡原因導致的失敗
MessageBox.Show(operate.ToMessageShowString());
}
}
|
更詳細的信息,可以參照源代碼里面的測試項目。