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的進階教學也完結了,多謝各位。

Compressor.io 縮圖大小網頁

網頁3大元素:文字、圖片、影像,三者不可或缺,特別是圖片及影像佔用大量網頁流量傳輸的資源,如何去減少這類資源,以提供瀏覽網頁的用戶更好的體驗,也是一門學問。在此,想分享一下如何壓縮圖片的檔案大小,而畫質也不會很差。
坊間有很多縮圖網站,例如TinyPNG、Compressor.io等等,經過我的多次測試,發現Compressor.io壓縮能力最高(個人意見),所以我會分享Compressor.io的縮圖方法。

網址: https://compressor.io/

以下是需要縮減檔案大小的香港天文台黃雨標誌(139KB),如果網頁用戶用電話瀏覽網頁時使用流動數據,網頁用戶一定會鬧‧‧‧



以下是Compressor.io主頁界面,按一下"Try it"就可以開始了。



有2種縮圖模式,一種Lossy,一種Lossless,很熟悉,像是中學教過的,於是找找資料。

#(Lossless)無損壓縮也即壓縮前和解壓縮後的數據完全一致。多數的無損壓縮都採用RLE行程編碼算法。(Lossy)有損壓縮意味著解壓縮後的數據與壓縮前的數據不一致。在壓縮的過程中要丟失一些人眼和人耳所不敏感的圖像或音頻信息,而且丟失的信息不可恢復。幾乎所有高壓縮的算法都採用有損壓縮,這樣才能達到低數據率的目標。丟失的數據率與壓縮比有關,壓縮比越小,丟失的數據越多,解壓縮後的效果一般越差。此外,某些有損壓縮算法採用多次重複壓縮的方式,這樣還會引起額外的數據丟失。#
通常,我會選取Lossless,壓縮完後的檔案會比較好,失真不會太過嚴重,當然Lossy都不會有大問題。之後按下"Select File"按鈕,選取想要被壓縮的圖片,我選擇了一個黃雨標誌。


結果出現了!!!

檔案大小由139KB,大幅縮減至29.03KB,足足減少了110KB,少了400%,而圖片也沒有失真,以後做圖之後都一定要來壓圖才行。按一下"Download your file"就可打包下載,也有Google Drive,Dropbox選項,非常方便。

2016年2月22日 星期一

C# 及 VB 轉換

該死的Visual Studio,為何你提供這麼多的Language給程序員選擇,導致我有時要寫C#,一時VB。。。辛苦死了。幸好,網上有一個轉換Tool,可快速將VB Code轉成C#,C#轉成VB。(但不是100%能夠成功),以下是網址:
http://converter.telerik.com/

在WEB的左方輸入VB/C# Code,再選選個DropDown,再按個"Convert Code"按鈕,右面就會出現相對應的Code,真是方便至極,愛死Telerik了。

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";


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

2016年2月12日 星期五

Windows Server 2012架設NTP時間伺服器(2) 於services.msc 啟動服務

於regedit.exe修改 數值

於桌面右上角按鍵至"Search",並輸入"services.msc"

於Services視窗連按兩下"Windows Time"

於"Windows Time Properties"視窗"General"內Startup type選擇"Automatic"後按下"Start"按鈕,待Service status轉為Running時, 然後按一下"Apply", 最後按一下"OK"

"Windows Time"服務狀態為"Running"即可


步驟2 (services.msc 啟動 服務) 完成!!

Windows Server 2012架設NTP時間伺服器(1) 於regedit.exe修改數值

架設NTP Server顧明思義,就是Sync Time,特別是工程信息公司,Time Sync是絕對、一定、十分重要的,因為資料轉換過程的紀錄時間不容有偏差(~300ms還是可接受的)。以下就是教學:

於regedit.exe修改 數值

於桌面右上角按鍵至"Search",並輸入"regedit"及選擇"regedit.exe"

出現"Registry Editor"後,於左方樹狀列選取(HKEY_LOCAL_MACHINE --> SYSTEM -->CurrentControlSet --> Services --> W32Time)

更改資料夾"Config"的數值至下圖

AnnounceFlags

5

MaxPosPhaseCorrection

ffffffff

MaxNegPhaseCorrection

ffffffff

 

更改資料夾"Parameters"的數值至下圖

NtpServer

stdtime.gov.hk,0x1 time.windows.com,0x2

Type

NTP



更改資料夾"TimeProviders"內"NtpClient"的數值至下圖

SpecialPollInterval

180



更改資料夾"TimeProviders"內"NtpServer"的數值至下圖


Enabled

1

 



步驟1 (regedit.exe修改 數值) 完成!!