顯示具有 Winform 標籤的文章。 顯示所有文章
顯示具有 Winform 標籤的文章。 顯示所有文章

2016年3月29日 星期二

C#連接OPC Server (Intouch 2014 R2 using Suitelink)

最近煩惱如何C#連接Intouch 2014 R2,並且要將Tag value寫入Intouch當中,對於我這個Intouch的菜鳥,這是非常的困難,但經過經驗人仕指導一番後就成功了。以下是一個分享:
先下載opcdaauto.dll,作為Project的Reference,再寫入下面經過改良的Class,當中不難看出Class包含連接OPC Server、創建Client Group及寫入Tag Value。
using OPCAutomation;

public class OPC
{
OPCServer KepServer;
OPCGroups KepGroups;
OPCGroup KepGroup;
OPCItems KepItems;
OPCItem KepItem;
string strHostIP = "";
string strHostName = "";
int itmHandleClient = 0;

public bool GetLocalServer()
{
  IPHostEntry IPHost = Dns.GetHostEntry(Environment.MachineName);
  if (IPHost.AddressList.Length > 0)
  {
    strHostIP = IPHost.AddressList[0].ToString();
  }
  else
  {
    MessageBox.Show("GetLocalServer Error 1");
    return false;
  }
  IPHostEntry ipHostEntry = Dns.GetHostEntry(strHostIP);
  strHostName = ipHostEntry.HostName.ToString();
  try
  {
   KepServer = new OPCServer();
   object serverList = KepServer.GetOPCServers();
   KepServer.Connect(((Array)serverList).GetValue(1).ToString(),"localhost");
  }
  catch(Exception ex)
  {
   MessageBox.Show("GetLocalServer Error 2 " + ex.Message);
   return false;
  }
  return true;
}

public bool CreateGroup()
{
  try
  {
    KepGroups = KepServer.OPCGroups;
    KepGroup = KepGroups.Add("MaxsonProgram");
    SetGroupProperty();
    KepGroup.DataChange += new   DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
   KepGroup.AsyncWriteComplete += new   DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete);
   KepItems = KepGroup.OPCItems;
  }
  catch (Exception err)
  {
   MessageBox.Show(err.Message, "CreateGroup提示信息", MessageBoxButtons.OK,     MessageBoxIcon.Warning);
   return false;
  }
  return true;
}

public void SetGroupProperty()
{
  KepServer.OPCGroups.DefaultGroupIsActive = true;
  KepServer.OPCGroups.DefaultGroupDeadband = 0;
  KepGroup.UpdateRate = 1000;
  KepGroup.IsActive = true;
  KepGroup.IsSubscribed = true;
}

void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
{
  MessageBox.Show("Success Write");
}

void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
}

public void opcWrite(string tagname, string value)
{
  try
  {
   Array Errors;
   int cancelID;
   KepItem = KepItems.AddItem(tagname, itmHandleClient);
   int[] temp2 = new int[2] { 0, KepItem.ServerHandle };
   Array serverHandles = (Array)temp2;
   object[] valueTemp = new object[2] { "", value };
   Array values = (Array)valueTemp;
   KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);
   KepItems.Remove(KepItems.Count, ref serverHandles, out Errors);
   GC.Collect();
 }
 catch (Exception ex) { MessageBox.Show(ex.Message); }
 }
}

如果想調用OPC Class,可依下面作為參考。
OPC opc = new OPC();
if (opc.GetLocalServer())
{
 if (opc.CreateGroup())
 {
   opc.opcWrite("SuiteLink.Topic.Test02", "30");   
   opc.opcWrite("SuiteLink.Topic.Rainfall_Value", "10.33"); 
   opc.opcWrite("SuiteLink.Topic.Rainfall_X", "123.456");  
   opc.opcWrite("SuiteLink.Topic.Rainfall_Y", "654.321");  
   opc.opcWrite("SuiteLink.Topic.WinterMode", "1");  
   opc.opcWrite("SuiteLink.Topic.NormalMode", "0");  
   opc.opcWrite("SuiteLink.Topic.TriggerLevel", "1.6");  
   opc.opcWrite("SuiteLink.Topic.RainingState", "0");
  }
} 

Intouch Setting如下:




Visual Studio Setting如下(.net 4.5 + 32 bit CPU):(必須,我試了很多次,這是必須,否則會連接不上,這可是的花了很多時間得出來的結論)


如果設定成功及運行C# Application,你會在Intouch Viewer中看到Tag Value會改變,亦即代表你成功了。

2016年3月2日 星期三

C# Backgroundworker進階教學(Thread.Sleep的正確使用)

System.Threading.Thread.Sleep相信各位Winform的程式員都知道是什麼,就是停止運作一定的時間,但作為一個已進化的菜鳥,不能夠在正常情況可使用,最好是在BackgroundWorker內用,就能夠神不知鬼不覺,因為當你不在BackgroundWorker內使用,該死的界面就會顯示(Not Responding)的字句,客戶就會走來找你,問是不是死機啊‧‧‧以下是一個簡單的例子。

1‧創建一個WINFORM界面,並新增4個Control─label1、button1、toolStripProgressBar1、backgroundworker1

*backgroundworker1不是User Interface*

2‧background新增Event Handler

WorkerReportsProgress一定要set成true,否則不能加入ProgressChanged Event。
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);

3‧ProgressChanged Event

主要是紀錄進度,除了toolStripProgressBar,toolStripLabel也是能夠更改的。
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   // The progress percentage is a property of e
   toolStripProgressBar1.Value = e.ProgressPercentage;
}

4‧DoWork Event

DoWork這裡會轉換label1的文字,ProgressBar亦會紀錄進度,Thread.Sleep在這裡使用。
在function內接觸Control類的,需要加入Invoke(Action),才可運行,否則會彈出跨線程錯誤。
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   Args arg = (Args)e.Argument;
   string a = arg.a;
   string b = arg.b;
   backgroundWorker1.ReportProgress(0);
   label1.Invoke(new Action(() => { label1.Text = b; }));
   backgroundWorker1.ReportProgress(30);
   Thread.Sleep(1000);
   backgroundWorker1.ReportProgress(60);
   label1.Invoke(new Action(() => { label1.Text = a; }));
   backgroundWorker1.ReportProgress(100);
   Thread.Sleep(1000);
   backgroundWorker1.ReportProgress(0);
   button1.Invoke((Action)(() => button1.Enabled = true));
}

5‧Args Class

在DoWork拿取Control的數值、文字是比較困難的,最好的方法是由外面掉Parameter入DoWork Function,所以先創建一個Class。
簡簡單單,看得明白就好。
internal class Args
{
   internal string a { get; set; }
   internal string b { get; set; }
}

5‧Button1 Action

Button1用來啟動BackgroundWorker,而且將剛才的Class放入DoWork這個Function裡面。

private void button1_Click(object sender, EventArgs e)
{
   button1.Enabled = false;
   var args = new Args() { a = "YES", b = "NO" };
   backgroundWorker1.RunWorkerAsync(args);
}

6‧結果

運行時可以見到label1由"No"變成"Yes",而ProgressBar亦會上升,為時大約5秒,而且UI不會出現Not Responding字樣,所以成功了。



這個Backgroundworker、progressbar的進階教學也完結了,多謝各位。

2016年2月20日 星期六

C# Winform 工作計時

最近要做一個Tool,需要Read 26個txt file,每個file有2500行的Data,並紀錄所需時間。原本我以為要用到Timer,但是原來是不用的。 System.Diagnostics 這個Class內有一個好用的Stopwatch,能夠像計時器一樣計出準確時間,而且Coding也是僅僅數句,真是省了很多功夫,數小時就能搞掂(當然包UI,包埋點read)。

下面是Stopwatch的Coding,將所需時間放進一個Label裡面。 var watch = Stopwatch.StartNew();

//Do Work

watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
label1.Text = (dlapsedMs / 1000.00).ToString() + " s";


希望以後不會不記得!!!