去過工廠或者倉庫的都知道,在工廠或倉庫裡面,會有很多不同的流水線,大部分的工廠或倉庫,都會在不同流水線的不同工位旁邊安裝一台電腦,一方面便于工位上的師傅把産品的重要資訊錄入系統,便于公司系統資料采集分析。另一方面嚴謹的工廠或倉庫也會在每個工位上安裝攝像頭,用于采集或監控流水線上勞工的操(是)作(否)習(偷)慣(懶)。
好了,閑話少說,咱們直入主題吧!
本系統監控系統,主要核心是使用AForge.NET提供的接口和插件(dll),感興趣的朋友也可以去他們官網檢視文檔http://www.aforgenet.com/framework/documentation.html
Talk is cheap,show me the code!
系統初始化時,首先檢查工位的機台是否開啟了攝像頭,具體檢測代碼如下:
/// <summary>
/// 監控bind
/// </summary>
private void bind()
{
try
{
FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count <= 0)
{
MessageBox.Show("請連接配接攝像頭");
return;
}
else
{
CloseCaptureDevice();
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.VideoResolution = videoSource.VideoCapabilities[0];
sourcePlayer.VideoSource = videoSource;
sourcePlayer.Start();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
好了,攝像頭沒問題,咱在檢查網絡是否正常(這事兒可以交給運維,當然也可以通過程式控制,具體校驗網絡代碼比比皆是,此處忽略,如有興趣的朋友可以在公衆号Call我一起探讨),至于為什麼要校驗網絡,一部分是用于機台系統的資料采集,另一部分就是錄制的視訊檔案不可能存儲在工位機台上,不然流水線和工位足夠多,豈不是一個工位一個幾天的檢視視訊監控嘛!咱這都是智能化時代,錄制的視訊可以儲存在本地,不過為了友善起見,需要定時清理,定時上傳到伺服器便于上司審查。視訊上傳到伺服器一般用到最多的莫非兩種情況,1.網絡足夠穩定,足夠快的可以直接和伺服器開個磁盤映射(共享目錄),視訊錄制完後系統直接剪切到伺服器儲存即可。2.把不同時段錄制的視訊先存儲到本地,然後單獨開發個定時任務FTP定時上傳即可。今天先跟大家分享下第一種方法,第二種方法也比較簡單,有興趣的朋友可以公衆号call我一起探讨。
不知不覺又扯了一堆廢話,都是實在人,直接上源碼吧:
/// <summary>
/// 開啟或者關閉程式後将多餘檔案copy到相應目錄,并開啟磁盤映射上傳到共享目錄
/// </summary>
private void CopyFilesToServer()
{
try
{
//周遊 目前PC檔案夾外是否存在視訊檔案,如存在,移動到目标目錄
string newPath = path + MacAddressPath + @"-Video\";
if (!Directory.Exists(newPath)) Directory.CreateDirectory(newPath);
//将上一次最後一個視訊檔案轉入目錄
var files = Directory.GetFiles(path, "*.wmv");
foreach (var file in files)
{
FileInfo fi = new FileInfo(file);
string filesName = file.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
fi.MoveTo(newPath + filesName);
}
}
catch (Exception ex)
{
//TODO:異常抛出
}
finally
{
uint state = 0;
if (!Directory.Exists("Z:"))
{
//計算機名
string computerName = System.Net.Dns.GetHostName();
//為網絡共享目錄添加磁盤映射
state = WNetHelper.WNetAddConnection(computerName + @"\" + netWorkUser, netWorkPwd, netWorkPath, "Z:");
}
if (state.Equals(0))
{
//本地磁盤視訊檔案copy到網絡共享目錄
CopyFolder(path + MacAddressPath + @"-Video\", zPath);
}
else
{
WNetHelper.WinExec("NET USE * /DELETE /Y", 0);
throw new Exception("添加網絡驅動器錯誤,錯誤号:" + state.ToString());
}
}
}
其中CopyFolder方法代碼如下:
#region 通過共享網絡磁盤映射的方式,講檔案copy到指定網盤
/// <summary>
/// 通過共享網絡磁盤映射的方式,講檔案copy到指定網盤
/// </summary>
/// <param name="strFromPath"></param>
/// <param name="strToPath"></param>
public static void CopyFolder(string strFromPath, string strToPath)
{
//如果源檔案夾不存在,則建立
if (!Directory.Exists(strFromPath))
{
Directory.CreateDirectory(strFromPath);
}
if (!Directory.Exists(strToPath))
{
Directory.CreateDirectory(strToPath);
}
//直接剪切moveto,本地不留副本
string[] strFiles = Directory.GetFiles(strFromPath);
//循環剪切檔案,此處循環是考慮每日工作站最後一個檔案無法存儲到根目錄,導緻出現兩個視訊檔案的問題
for (int i = 0; i < strFiles.Length; i++)
{
//取得檔案名,隻取檔案名,位址截掉。
string strFileName = strFiles[i].Substring(strFiles[i].LastIndexOf("\\") + 1, strFiles[i].Length - strFiles[i].LastIndexOf("\\") - 1);
File.Move(strFiles[i], strToPath + "DT-" + strFileName);
}
}
#endregion
做完機台檢查工作,也做好了視訊傳輸的工作,接下來就是視訊錄制的主角戲了,完整錄制視訊源碼如下(有疑問的朋友可以公衆号-聯系我一起探讨):
/// <summary>
/// videosouceplayer 錄像
/// </summary>
/// <param name="sender"></param>
/// <param name="image"></param>
private void sourcePlayer_NewFrame(object sender, ref Bitmap image)
{
try
{
//寫到螢幕上的時間
g = Graphics.FromImage(image);
SolidBrush drawBrush = new SolidBrush(Color.Yellow);
Font drawFont = new Font("Arial", 6, System.Drawing.FontStyle.Bold, GraphicsUnit.Millimeter);
int xPos = image.Width - (image.Width - 15);
int yPos = 10;
string drawDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
g.DrawString(drawDate, drawFont, drawBrush, xPos, yPos);
//save content
string videoFileName = dt.ToString("yyyy-MM-dd HHmm") + ".wmv";
if (TestDriveInfo(videoFileName)) //檢測硬碟空間足夠
{
if (!stopREC)
{
stopREC = true;
createNewFile = true; //這裡要設定為true表示要建立新檔案
if (videoWriter != null) videoWriter.Close();
}
else
{
//開始錄像
if (createNewFile)
{
//第二次錄像不一定是第二次開啟軟體時間(比如:連續多小時錄制),是以應該重新給新錄制視訊檔案重新指派命名
dt = DateTime.Now;
videoFileFullPath = path + dt.ToString("yyyy-MM-dd HHmm") + ".wmv";//videoFileName;
createNewFile = false;
if (videoWriter != null)
{
videoWriter.Close();
videoWriter.Dispose();
}
videoWriter = new VideoFileWriter();
//這裡必須是全路徑,否則會預設儲存到程式運作根據錄下了
videoWriter.Open(videoFileFullPath, image.Width, image.Height, 30, VideoCodec.WMV1);
videoWriter.WriteVideoFrame(image);
}
else
{
if (videoWriter.IsOpen)
{
videoWriter.WriteVideoFrame(image);
}
if (dt.AddMinutes(1) <= DateTime.Now)
{
createNewFile = true;
//modify by stephen,每次寫入視訊檔案後,即刻更新結束時間戳,并存入指定檔案夾(目的:如果隻有關閉的時候處理此操作,就會出現大于1小時的視訊檔案無法更新結束時間戳,且無法轉入指定檔案夾)
if (videoWriter != null)
{
videoWriter.Close();
videoWriter.Dispose();
}
string newPath = path + MacAddressPath + @"-Video\";
if (!Directory.Exists(newPath)) Directory.CreateDirectory(newPath);
string newStr = newPath + dt.ToString("yyyyMMddHHmm") + "-" + DateTime.Now.ToString("yyyyMMddHHmm") + ".wmv";
FileInfo fi = new FileInfo(videoFileFullPath);
fi.MoveTo(newStr);
////轉移到網路目錄
//CopyFilesToServer();
}
}
}
}
}
catch (Exception ex)
{
videoWriter.Close();
videoWriter.Dispose();
}
finally
{
if (this.g != null) this.g.Dispose();
}
}
其中TestDriveInfo方法是用來擷取儲存視訊的磁盤資訊的,具體代碼如下:
#region 擷取儲存視訊的磁盤資訊
/// <summary>
/// 擷取儲存視訊的磁盤資訊
/// </summary>
bool TestDriveInfo(string n)
{
try
{
DriveInfo D = DriveInfo.GetDrives().Where(a => a.Name == path.Substring(0, 3).ToUpper()).FirstOrDefault();
Int64 i = D.TotalFreeSpace, ti = unchecked(50 * 1024 * 1024 * 1024);
if (i < ti)
{
DirectoryInfo folder = new DirectoryInfo(path + MacAddressPath + @"-Video\");
//modify by stephen,驗證目前指定檔案夾是否存在元素
if (folder.Exists)
{
var fisList = folder.GetFiles("*.wmv").OrderBy(a => a.CreationTime);
if (fisList.Any())
{
List<FileInfo> fis = fisList.ToList();
if (fis.Count > 0 && fis[0].Name != n)
{
File.Delete(fis[0].FullName);
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "處理硬碟資訊出錯");
return false;
}
return true;
}
#endregion
當然,如果工位師傅錄入産品資訊有疑問的話,也可以利用系統截圖來保留證據,這個是我自己畫蛇添足的功能,反正是為了友善嘛,别耽誤了工位師傅的辦事效率,利用攝像頭截圖代碼如下:
try
{
string pathp = $@"{Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)}\";
if (!Directory.Exists(pathp)) Directory.CreateDirectory(pathp);
if (sourcePlayer.IsRunning)
{
BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
sourcePlayer.GetCurrentVideoFrame().GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
PngBitmapEncoder pE = new PngBitmapEncoder();
pE.Frames.Add(BitmapFrame.Create(bitmapSource));
string picName = $"{pathp}{DateTime.Now.ToString("yyyyMMddHHmmssffffff")}.jpg";
if (File.Exists(picName))
{
File.Delete(picName);
}
using (Stream stream = File.Create(picName))
{
pE.Save(stream);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
代碼比較簡單,就不寫備注了。當然部署系統的時候也不是一帆風順,有的工廠或者倉庫會購買第三方的攝像頭,礙于工位環境,攝像頭有可能與機台角度偏差較大,是以我又畫蛇添足的了校驗攝像頭的小功能,可以左右90°上下180°畫面翻轉,具體代碼如下:
#region 設定攝像頭旋轉調整
if (image != null)
{
RotateFlipType pType = RotateFlipType.RotateNoneFlipNone;
if (dAngle == 0)
{
pType = RotateFlipType.RotateNoneFlipNone;
}
else if (dAngle == 90)
{
pType = RotateFlipType.Rotate90FlipNone;
}
else if (dAngle == 180)
{
pType = RotateFlipType.Rotate180FlipNone;
}
else if (dAngle == 270)
{
pType = RotateFlipType.Rotate270FlipNone;
}
// 實時按角度繪制
image.RotateFlip(pType);
}
#endregion
當然,站在公司角度,為了防止工位師傅手誤(誠心)關掉視訊監控程式,我們也可以從程式的角度來防患于未然,比如禁用程式的關閉按鈕,禁用工具欄右鍵程式圖示關閉程式的操作。
我們可以重寫視窗句柄來防止,具體代碼如下:
#region 視窗句柄重寫,禁用窗體的關閉按鈕
private const int CP_NOCLOSE_BUTTON = 0x200;
protected override CreateParams CreateParams
{
get
{
CreateParams myCp = base.CreateParams;
myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON;
return myCp;
}
}
至此,系統代碼告一段路,一起來看看軟體效果吧!(請自動忽略視訊内容,以及筆記本攝像頭帶來的渣渣像素

)
最後,由于系統引用檔案較多,壓縮後源碼檔案仍然很大,如果有需要源碼的朋友,可以微信公衆号聯系部落客,源碼可以免費贈予~!有疑問的也可以CALL我一起探讨,最最後,如果覺得本篇博文對您或者身邊朋友有幫助的,麻煩點個關注!贈人玫瑰,手留餘香,您的支援就是我寫作最大的動力,感謝您的關注,期待和您一起探讨!再會!(公衆号回複:視訊或者監控擷取源碼)
作者:Stephen-kzx
出處:http://www.cnblogs.com/axing/
公衆号:會定時分享寫工作中或者生活中遇到的小遊戲和小工具源碼。有興趣的幫忙點下關注!感恩!
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。