數字孿生項目實戰,WPF與Unity結合開發之路(一)

?數字孿生項目實戰,WPF與Unity結合開發之路(一)

數字孿生項目實戰,WPF與Unity結合開發之路(一)

作 ? 者:水娃

  • 嗨大家好,我是一名骨灰級的WPF開發者,我叫水娃。

  • 這次主要是向大家講解一個WPFUnity相結合來實現WPF3D的交互項目。此前一直做WPF開發,但是有時候要實現一些3D過程的時候,用WPF做就很麻煩。經過不斷探索,作者總結了一套合理的WPFUnity通訊和嵌入方式,如果能用不同的技術相組合,用各自的技術做他擅長的方向,那既能達到產品需求又可以避免技術開發難點,是不是要比單用一門技術來實現要好很多呢?

  • 項目的起因是要做一個數字孿生項目,按照白皮書的解釋,數字孿生分為幾個階段:

    • 1.虛實映射

    • 2.實時同步

    • 3.共生演進

    • 4.閉環優先。

  • 這里由于版權原因,我們只開源到第二部分實時同步階段,屬于集成前的測試程序,但是整體的集成方式和通訊過程已經全部實現了。項目主要實現對一個風機電廠中各種風機的監測和控制,由于風機的采集協議是用的Modbus,所以采集這部分選擇WPF開發,SQLite存儲。但是要用3D來實現風機的表現,比如風速、轉向、掉線離線、不同風速對風機的影響,這部分如果用WPF來表現,那就很麻煩了,所以最終決定采用Unity來開發這部分。最后WPF里嵌入Unity來最終項目呈現。演示效果如下。

  • 我們分為三部分來開發:

    • 1.WPF部分

    • 2.Unity部分

    • 3.集成部分

(一)、WPF 測試界面如下:

dbeac93e50befe48103049fcf7a21f3a.png
  • 先簡單介紹一下Modbus協議,Modbus是一個現場總線協議,應用在電子控制器上,可以實現控制器相互之間、PC到控制器的通訊。支持多種電氣接口(RS232、RS422、RS485、RJ45)和多種傳輸介質(雙絞線、網線)。主要有串口和網口方式,串口(電腦后面的串口孔,PC只有232串口的,所以需要買串口轉換器,才能用485協議)主要是用RS485協議一主多從模式,傳輸格式有ModbusAsciiModbusRTU;

  • 網口(電腦后面插網線的口)主要是ModbusTCPModbusUDP,傳輸格式和串口ModbusRTU的相同。Modbus中數據存儲類型為bit(bool),byte(8位),word(16位),dword(32位). 這幾個類型的主要區別是存儲的長度不同,類似C#里的intdouble。所謂的上位機一般都是用別人寫好的庫,連好硬件,然后根據地址表,從硬件中讀出來對應的數據,然后再解析出來。 因為C#的最小單位是byte,所以我們讀取完之后,一切都是byte[], 一定要寫好解析過程,不然就會出錯。

  • Modbus這部分解決了,數據有了,下一步就是要從WPF發送給Unity,作者選擇了Socket協議,別的一些網絡協議也可以,但是Socket比較成熟,作者用的比較熟悉。最終封裝的類庫代碼如下

public?class?socketServer{public?class?StateObject{//?Client?socket.public?Socket?workSocket?=?null;//?Size?of?receive?buffer.public?const?int?BufferSize?=?1024;//?Receive?buffer.public?byte[]?buffer?=?new?byte[BufferSize];}public?class?ConnectionServer{public?static?ConnectionServer?Instance?=>?_instance;private?static?readonly?ConnectionServer?_instance?=?new?ConnectionServer();///?<summary>///?監聽線程///?</summary>public?Socket?listenSocket;///?<summary>///?tcp客戶端對象///?</summary>public?Socket?clientSocket?=?null;///?<summary>///?異步發送數據///?</summary>///?<returns></returns>public?int?Send(byte[]?byteMessage,?int?size){int?offset?=?0;try{return?SendBytes(byteMessage,?size,?ref?offset);}catch?(Exception?ex){Console.WriteLine($"發送出現異常{ex.Message.ToString()}");return?0;}}private?int?SendBytes(byte[]?byteMessage,?int?size,?ref?int?offset){if?(clientSocket?!=?null){while?(offset?<?size){int?n?=?clientSocket.Send(byteMessage,?offset,?size?-?offset,?SocketFlags.None);if?(n?>?0){offset?+=?n;}else{Console.WriteLine("發送數據失敗");break;}}return?offset;}return?0;}public?void?Start(string?ipServer,?int?portServer){IPEndPoint?ipEnd?=?new?IPEndPoint(IPAddress.Parse(ipServer),?portServer);//創建監聽listenSocket?=?new?Socket(ipEnd.AddressFamily,?SocketType.Stream,?ProtocolType.Tcp);//監聽該IPlistenSocket.Bind(ipEnd);//設置監聽隊列中最多可容納的等待接受的傳入連接數listenSocket.Listen(100);Console.WriteLine($"開始監聽:{ipServer}:{portServer}");//開始接受客戶端連接while?(true){clientSocket?=?listenSocket.Accept();var?ip?=?((IPEndPoint)clientSocket.RemoteEndPoint).Address;var?port?=?((IPEndPoint)clientSocket.RemoteEndPoint).Port;var?appkey?=?$"{ip}^{port}";if?(clientSocket.Connected){Console.WriteLine($"{appkey}連接到了服務端");try{//?開始異步接受數據SetupReceiveCallback();}catch?(Exception?ex){Console.WriteLine("Socket異步方式接收數據發生異常:{0}",?ex.StackTrace);}}else{Console.WriteLine("連接建立失敗");}}}///?<summary>///?開始用Socket異步方式接收數據。///?</summary>private?void?SetupReceiveCallback(){if?(clientSocket?!=?null){try{StateObject?state?=?new?StateObject();state.workSocket?=?clientSocket;clientSocket.BeginReceive(state.buffer,?0,?StateObject.BufferSize,?SocketFlags.None,new?AsyncCallback(OnReceive),?state);}catch?(Exception?ex){Console.WriteLine("Socket異步方式接收數據發生異常:{0}",?ex.StackTrace);}}else{Console.WriteLine("異步接收回報消息socket為null");}}///?<summary>///?異步接收回調///?</summary>///?<param?name="ar"></param>private?void?OnReceive(IAsyncResult?ar){try{if?(clientSocket?!=?null){StateObject?state?=?(StateObject)ar.AsyncState;Socket?client?=?state.workSocket;//?Read?data?from?the?remote?device.int?bytesRead?=?client.EndReceive(ar);if?(bytesRead?>?0){byte[]?result?=?new?byte[bytesRead];Buffer.BlockCopy(state.buffer,?0,?result,?0,?bytesRead);var?msg?=?Encoding.UTF8.GetString(result);Console.WriteLine("收到消息:"?+?msg);MsgCenter.Receive(msg);//?Send(result,?result.Length);SetupReceiveCallback();}else{Console.WriteLine("異步接受數據bytesRead為0");}}}catch?(Exception?ex){Console.WriteLine($"異步接受數據異常{ex.Message}");}}}}
  • 這個類沒有處理粘包情況,都是直接發直接收和解析,如果不是高頻通訊,比如毫秒級的通訊,其實粘包情況很少發生。

  • 一般處理粘包都是一個消息分為消息頭+消息體+消息尾巴,或者簡單一點直接消息頭+消息體的形式。消息頭里面一般會有序號和消息體的長度,方便接收端進行處理。由于項目通訊頻率不高,每臺風機是1s通訊一次,也就是1s WPF會把一個風機數據發送給Unity,一個程序中最多有10臺風機,所以1s最多發送10次。

  • 因為都是本機通訊,經過大量測試,沒有出現粘包的情況,所以測試通訊類只封裝了發送和接收,實際使用起碼要封裝斷線重連,心跳檢測才能真正使用。

(二)、 ?然后定義通訊格式,代碼如下:

public??class?MessageModel{///?<summary>///?id///?</summary>public?string?msid;///?<summary>///風機名字///?</summary>public?string?epname;///?<summary>///消息類型///?</summary>public?string?msg_type;///?<summary>///?狀態字服務器///?</summary>public?string?severztz;///?<summary>///?控制字服務器///?</summary>public?string?severkzz;///?<summary>///?偏航修正量,絕對值///?</summary>public?string?severphxzl;///?<summary>///?風速修正量,相對值///?</summary>public?string?severfxxzl;///?<summary>///?計數器///?</summary>public?string?severjzq;///?<summary>///??reserved///?</summary>public?string?severreserved;///?<summary>///??狀態字///?</summary>public?string?ztz;///?<summary>///??計數器///?</summary>public?string?jsq;///?<summary>///?風速///?</summary>public?string?fs;///?<summary>///?風向///?</summary>public?string?fx;///?<summary>///?機艙方位角///?</summary>public?string?jcfwj;///?<summary>///?雷達風速///?</summary>public?string?ldfs;///?<summary>///?雷達風向///?</summary>public?string?ldfx;///?<summary>///?雷達狀態字1///?</summary>public?string?ldztzone;///?<summary>///?雷達狀態字2///?</summary>public?string?ldztztwo;///?<summary>///雷達判斷數據是否有效///?</summary>public?string?ldsfyx;///?<summary>///雷達掃描層///?</summary>public?string?dyldsmc;///?<summary>///軸向風速///?</summary>public?string?zxfs;///?<summary>///水平風速///?</summary>public?string?spfs;///?<summary>///垂直風速///?</summary>public?string?czfs;///?<summary>///光束1///?</summary>public?string?vlos1;///?<summary>///光束2///?</summary>public?string?vlos2;///?<summary>///光束3///?</summary>public?string?vlos3;///?<summary>///光束4///?</summary>public?string?vlos4;///?<summary>///光束5///?</summary>public?string?vlos5;///?<summary>///光束6///?</summary>public?string?vlos6;///?<summary>///光束7///?</summary>public?string?vlos7;///?<summary>///光束8///?</summary>public?string?vlos8;///?<summary>///光束9///?</summary>public?string?vlos9;///?<summary>///光束10///?</summary>public?string?vlos10;///?<summary>///光束11///?</summary>public?string?vlos11;///?<summary>///光束12///?</summary>public?string?vlos12;///?<summary>///光束13///?</summary>public?string?vlos13;///?<summary>///光束14///?</summary>public?string?vlos14;///?<summary>///光束15///?</summary>public?string?vlos15;///?<summary>///光束16///?</summary>public?string?vlos16;///?<summary>///光束測量有效性///?</summary>public?string?gsclyxx;///?<summary>///reserved1///?</summary>public?string?reserved1;///?<summary>///reserved2///?</summary>public?string?reserved2;///?<summary>///reserved3///?</summary>public?string?reserved3;}
  • 其中的關鍵字段是epnamemsg_type,一個是風機名字,用來區分不同風機,一個是msg_type用來區分不同消息。其余字段都是用來控制風機的狀態。

(三)、 ?Unity部分:

71086bfc91e590241c609c8a22572e26.png
  • 首先,我們需要建立地形,這個使用Unity內置的Terrain,就和WPF內置的控件一樣,拖進去進去微調,然后拖進去風機模型,進行位置調整。調整完效果如下:

1973fb50d20c44dca370ba4510c48af3.png
  • 然后我們開始制作圖表,這個圖表的制作方式其實和WPF寫界面是大同小異的。我們要實現的大屏界面如下:

2c2d4b9f99220058445b7a640c79b0ee.png
  • 首先實現最上面的部分,先把Unity設置為2D模式,然后增加一個Image控件和一個Text控件,Image控件選擇背景圖片,Text輸入文字。標題欄就形成了,如下

356c4e47bdf1775e182144a178b66485.png
  • 下面的風機總數那幾個圓形圖表的開發方式也類似,如下

cca50b285598b18c57f8299927cfdf0b.png
  • 下面的幾個圖表也是類似的開發方式,是不是發現很簡單?

  • 甚至比WPF的界面開發也要簡單,有時候高手和我們的差距,就是他們懂很多我們不懂的基礎知識,因為不懂,被高手一頓組合拳下來,老戳中我們的盲點,就覺得高手比較厲害,其實堅持學一學,我們也可以成為高手,雖然我現在也是個菜鳥。

  • 界面開發方式結束了,再來看看后臺代碼,同樣的我們也需要一個Socket接收類,如下:

public?class?ConnectionClient{public?static?ConnectionClient?Instance?=>?_instance;private?static?readonly?ConnectionClient?_instance?=?new?ConnectionClient();private?string?ip?{?get;?set;?}private?int?port?{?get;?set;?}///?<summary>///?當前狀態///?</summary>public?ConnState?CurrState?{?get;?set;?}///?<summary>///?tcp客戶端對象///?</summary>private?Socket?socket?=?null;///?<summary>///?上一個隊列數據中剩余字節長度///?</summary>private?byte[]?lastBytes;public?bool?InitConnection(){//創建SOCKETsocket?=?new?Socket(AddressFamily.InterNetwork,?SocketType.Stream,?ProtocolType.Tcp);CurrState?=?ConnState.Connecting;socket.NoDelay?=?true;socket.ReceiveTimeout?=?10000;socket.SendTimeout?=?5000;return?true;}public?bool?ConnectServer(string?ipServer,?int?portServer){if?(socket.Connected){CurrState?=?ConnState.Connected;Console.WriteLine("已經有了連接");return?true;}try{Console.WriteLine("開始建立連接");this.ip?=?ipServer;this.port?=?portServer;IPEndPoint?ipEnd?=?new?IPEndPoint(IPAddress.Parse(ip),?port);socket.Connect(ipEnd);if?(socket.Connected){//接受數據SetupReceiveCallback();CurrState?=?ConnState.Connected;}else{CurrState?=?ConnState.Disconnected;Console.WriteLine("連接不上");}}catch?(Exception?ex){CurrState?=?ConnState.Disconnected;Console.WriteLine("連接socket異常"?+?ex.Message.ToString());return?false;}if?(CurrState?==?ConnState.Connected){return?true;}return?false;}public?void?Reconnection(){while?(true){if?(CurrState?==?ConnState.Disconnected){InitConnection();ConnectServer(ip,?port);}else?{?}Thread.Sleep(3000);}}///?<summary>///?開始用Socket異步方式接收數據。///?</summary>protected?void?SetupReceiveCallback(){if?(socket?!=?null){try{StateObject?state?=?new?StateObject();state.workSocket?=?socket;socket.BeginReceive(state.buffer,?0,?StateObject.BufferSize,?SocketFlags.None,new?AsyncCallback(OnReceive),?state);}catch?(Exception?ex){Console.WriteLine("Socket異步方式接收數據發生異常:{0}",?ex.Message.ToString());}}else{Console.WriteLine("異步接收回報消息socket為null");}}///?<summary>///?異步接收回調///?</summary>///?<param?name="ar"></param>private?void?OnReceive(IAsyncResult?ar){CurrState?=?ConnState.Connected;try{StateObject?state?=?(StateObject)ar.AsyncState;Socket?client?=?state.workSocket;int?bytesRead?=?socket.EndReceive(ar);if?(bytesRead?>?0){byte[]?result?=?new?byte[bytesRead];Buffer.BlockCopy(state.buffer,?0,?result,?0,?bytesRead);var?msg?=?Encoding.UTF8.GetString(result);var?model?=?Newtonsoft.Json.JsonConvert.DeserializeObject<MessageModel>(msg);//?Debug.Log(msg);ReceiveAction(model);SetupReceiveCallback();}else{CurrState?=?ConnState.Disconnected;}}catch?(Exception?ex){Console.WriteLine($"發生異常{ex.Message.ToString()}");}}///?<summary>///?異步發送數據///?</summary>///?<returns></returns>public?int?Send(byte[]?byteMessage,?int?size){int?offset?=?0;try{return?SendBytes(byteMessage,?size,?ref?offset);}catch?(Exception?ex){Console.WriteLine($"發送出現異常{ex.Message.ToString()}");return?0;}}private?int?SendBytes(byte[]?byteMessage,?int?size,?ref?int?offset){if?(socket?!=?null){while?(offset?<?size){int?n?=?socket.Send(byteMessage,?offset,?size?-?offset,?SocketFlags.None);if?(n?>?0){offset?+=?n;}else{Console.WriteLine("發送數據失敗");break;}}return?offset;}return?0;}}void?Start(){ConnectionClient.Instance.InitConnection();ConnectionClient.Instance.ConnectServer(GlobalInit.basicInfoDict["ServerIP"],?int.Parse(GlobalInit.basicInfoDict["Port"]));ReceiveAction?=?Receive;
}void?Receive(MessageModel?msg){lock?(lockObject){message.Add(msg);}}
  • WPF不同之處在于,Unity每個腳本都有一個StartUpdate函數,所以更新界面的操作都要在這2個函數內執行。

  • Start是初始化時候執行的,Update是更新每一幀畫面時候執行的(Unity的渲染原理是根據計算機不同,1s內固定更新多少幀圖像,然后圖像連起來就形成了實時畫面)。

  • 所以最終我們改變界面的代碼要寫到Update內,他無法像WPF一樣可以自由切換UI線程。因此我們Socket收到的數據全部扔到了List<MessageModel> message里面,然后在Update里面判斷Message的信息,來對界面進行改變。如下:

void?Update(){lock?(lockObject){if?(message.Count?!=?0){for?(int?i?=?0;?i?<?message.Count;?i++){var?model?=?message[i];//Debug.Log(model.epname);switch?(model.msg_type){//sqlite?10,傳0?sql?server?11,傳1case?"10":GameManager.Instance.InitSql(0);message.Remove(model);break;case?"11":GameManager.Instance.InitSql(1);message.Remove(model);break;//100為實時數據?case?"100":GameManager.Instance.WeiLiu(model);RightPanel.Instance.SetInfo(model);RightPanel.Instance.SetFengJiState(model);message.Remove(model);break;//case?"200"://????GameManager.Instance.SetModelEffect(model.vlos1,?model.vlos2);//????break;}}message.Clear();}}}
  • 最終,Unity的開發過程總結一下就是:

    • 1.導入風機模型,記錄一個初始位置,然后隱藏風機,點擊新建時候克隆這個風機,輸入屬性后存儲到sqlite數據庫里面。

    • 2.寫好socket接受類,收到wpf傳來的消息,在update函數里面進行邏輯判斷,從而更改界面顯示。

(四)、集成部分

  • wpfunity開發完成后,來到了最終的集成環節。

  • 這里我們不采用網上那種方式,作者自己經過幾天研究,總結了一個比較好的集成方式。

  • 就是把unity固定的放到wpf界面的一個區域內,在移動和放大縮小wpf界面的時候,不斷的對unity程序進行移動和放大縮小,這樣整體保持了一致。

  • 主要是用幾個windows函數來操作:

[DllImport("user32.dll",?CharSet?=?CharSet.Auto)]static?extern?int?MoveWindow(IntPtr?hWnd,?int?x,?int?y,?int?nWidth,?int?nHeight,?bool?BRePaint);[DllImport("user32.dll",?CharSet?=?CharSet.Auto,?ExactSpelling?=?true)]public?static?extern?int?ShowWindow(IntPtr?hwnd,?int?nCmdShow);[DllImport("gdi32.dll")]private?static?extern?int?GetDeviceCaps(IntPtr?hDc,?int?nIndex);
  • 這里要注意,wpf界面要選擇window,不能選擇page,因為page頁面沒有句柄,無法把Unity程序設置為wpf界面的子元素。代碼如下:

public?MainWindow(){//靜態指定Current?=?this;//窗口關閉方式,主窗口一旦關閉,就關閉Application.Current.ShutdownMode?=?ShutdownMode.OnMainWindowClose;//初始化窗口大小,到屏幕的80%this.Height?=?SystemParameters.PrimaryScreenHeight?*?0.8d;this.Width?=?SystemParameters.PrimaryScreenWidth?*?0.8d;//讀取配置文件option?=?new?ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("AppSetting.json").Build().GetSection("config").Get<Option>();option.UnityDir?=?Environment.CurrentDirectory?+?"\\unity\\demo.exe";InitializeComponent();}private?void?Window_Loaded(object?sender,?RoutedEventArgs?e){//開始socket監聽Task.Run(()?=>?ConnectionServer.Instance.Start(option.Ip,?option.Port));//開啟渲染窗口,并設置父級RenderWindow.Current.OpenRenderWindow();//實際應該延時后建立socket后立馬發送選擇數據庫的信號//?Thread.Sleep(2000);//Button_Click_3(new?object(),?new?RoutedEventArgs());}//初始化,調整渲染窗口public?void?Init(){Window_SizeChanged(null,?null);}public?void?OpenRenderWindow(){//渲染程序路徑string?RenderExePath?=?MainWindow.option.UnityDir;//如果成功找到了渲染程序if?(!string.IsNullOrEmpty(RenderExePath)?&&System.IO.File.Exists(RenderExePath)){UnityEngine?=?Process.Start(RenderExePath);Thread.Sleep(3000);SetRenderWindow();}//沒找到渲染程序,就關閉else{MessageBox.Show("未找到渲染程序");//System.Windows.Application.Current.Shutdown();}}
  • 好了,這次的分享到這里結束,之后我會把開發過程詳細的寫出來,幫助大家手把手 的從01搭建這個項目,最終這個項目也會集成到web里面。如果有不懂的可以隨時加作者溝通,互相提高。二維碼在下方。

e163415fae68c4749749c171db7effa6.gifd80fe6109a299ecc3129a1e4733dbe09.png5bd4efed5bf68cf42733fb928cf96c13.jpeg

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280553.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280553.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280553.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

django17:importlib應用中間件代碼思想

轉載&#xff1a;https://www.cnblogs.com/alice-bj/articles/9276880.html 背景 仿django的中間件的編程思想 用戶可通過配置&#xff0c;選擇是否啟用某個組件/某個功能&#xff0c;只需要配置 eg:報警系統&#xff0c;發郵件&#xff0c;發微信 。。。 ( 根據字符串導入…

Python 全棧開發基礎

python面向對象 python異常處理 python網絡編程 python并發編程 臨時目錄 轉載于:https://www.cnblogs.com/fixdq/p/8883304.html

IBM連續兩年大數據市場占有率全球第一

ZDNet至頂網服務器頻道 04月22日 新聞消息:IBM 近日宣布&#xff0c;根據市場調研機構Wikibon最新研究報告《大數據供應商收益與市場預測》&#xff0c;IBM連續兩年實現大數據市場占有率第一&#xff0c;領跑報告中的70多家大數據供應商。同期&#xff0c;IBM年度報告也顯示&am…

idou老師教你學Istio06: 如何用istio實現流量遷移

流量遷移是流量管理的一個重要功能。istio提供的流量管理功能將流量從基礎設施擴展中解耦&#xff0c;支持動態請求路由&#xff0c;故障注入、超時重試、熔斷和流量遷移等。流量遷移的主要目的是將流量從微服務的某一版本的逐步遷移至另一個版本&#xff0c;如在新舊版本之間進…

用最少的代碼,寫一個完整MES項目(.NET6+WPF)

工業4.0時代&#xff0c;智能智造MES系統大行其道&#xff0c;然而基于.NET跨平臺的罕見&#xff01;這里有一套《.NET6WPF企業級MES實戰》教程&#xff0c;基于.NET6跨平臺開發&#xff0c;實現了MES多核心功能&#xff0c;尤其是開發框架完整&#xff0c;非常適合復用。這里分…

django18:auth模塊

Auth模塊 執行數據庫遷移命令后&#xff0c;自動生產多個表。 django_session auth_user 直接訪問admin路由&#xff0c;需要輸入用戶名和密碼&#xff0c;就是參考auth_user表 管理員用戶才能進入 創建超級用戶 createsuperuser from django.contrib import auth1.校驗用…

hulu dpp_什么是直播電視的Hulu,它可以代替您的有線電視訂閱嗎?

hulu dppStreaming cable replacements are becoming a much more appealing option for cable cutters across the board, with more choices available than ever before. Hulu’s Live TV option is a relative newcomer to the scene, but is it worth it? 對于全系列的電…

suse linux ssh遠程無法訪問問題

當正常安裝完Suse Linux Enterprise Server 11 sp1 時&#xff0c;無法通過SecureCRT或者PuTTY之類的終端程序進行連接。 折騰了一下&#xff0c;發現問題所在&#xff1a; 1、 需要關閉防火墻&#xff0c;如下圖在YAST里可以關閉&#xff0c;也可以使用下面命令行的方式&…

4.Linux的目錄結構

Linux的目錄結構 (1)"/"目錄 Linux文件系統的入口&#xff0c;也是出于最高一級的目錄 (2)"/bin" 基礎系統所需要的那些命令位于此目錄。也是最小系統所需要命令&#xff1b;比如ls、cp、mkdir等命令&#xff1b;功能和/usr/bin類似&#xff0c;這個目錄中…

Jade —— 源于 Node.js 的 HTML 模板引擎

2013-12-11 發布Jade —— 源于 Node.js 的 HTML 模板引擎 開源項目介紹 web 模板引擎 node.js jade 207.8k 次閱讀 讀完需要 69 分鐘54Jade 是一個高性能的模板引擎&#xff0c;它深受 Haml 影響&#xff0c;它是用 JavaScript 實現的&#xff0c;并且可以供 Node…

詐騙者如何偽造電子郵件地址,以及如何分辨

Consider this a public service announcement: Scammers can forge email addresses. Your email program may say a message is from a certain email address, but it may be from another address entirely. 考慮這是一項公共服務公告&#xff1a;詐騙者可以偽造電子郵件地…

如何對整個 WPF 應用程序進行灰度

如何對整個 WPF 應用程序進行灰度控件名&#xff1a;GrayscaleEffect作 者&#xff1a;WPFDevelopersOrg - 驚鏵原文鏈接[1]&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers 簡易源碼[2]框架使用.NET40&#xff1b;Visual Studio 2019;如果要實現灰度第一反是…

django19:項目開發流程

參考&#xff1a;https://www.bilibili.com/video/BV1QE41147hU?p831&spm_id_frompageDriver

React Native - FlexBox彈性盒模型

FlexBox布局 1. 什么是FlexBox布局? 彈性盒模型(The Flexible Box Module),又叫FlexBox,意為"彈性布局",旨在通過彈性的方式來對齊和分布容器中內容的空間,使其能適應不同屏幕,為盒模型提供最大的靈活性. ??Flex布局主要思想是: 讓容器有能力讓其子項目能夠改變其…

java虛擬機讀寫其他進程的數據

在java中&#xff0c;process類提供了如下3個方法&#xff0c;用于讓程序和其他子進程進行通信。 InputStream getErrorStream&#xff08;&#xff09;&#xff1a;獲取子進程的錯誤流。 InputStream getInputStream&#xff08;&#xff09;&#xff1a;獲取子進程的輸入流。…

release8_如何在Windows 8 Release Preview中將Chrome用作Metro瀏覽器

release8Windows 8 allows third-party browser to replace Internet Explorer in the Metro environment — except on Windows RT. You can use Google Chrome in Metro today, and Firefox for Metro is on the way. Windows 8允許第三方瀏覽器在Metro環境中替換Internet Ex…

html jQuery/bootstrap通過網絡bootcdn導入連接

網絡連接網址 https://www.bootcdn.cn/ <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"utf-8"><title>title</title><!-- Bootstrap --><link href"https://cdn.bootcdn.net/ajax/libs/twi…

Python深入類和對象

一. 鴨子類型和多態 1.什么是鴨子類型&#xff1a; 在程序設計中&#xff0c;鴨子類型&#xff08;英語&#xff1a;Duck typing&#xff09;是動態類型和某些靜態語言的一種對象推斷風格。"鴨子類型"像多態一樣工作&#xff0c;但是沒有繼承。“鴨子類型”的語言是這…

linux中/usr下文件權限修改setuid導致的問題

2019獨角獸企業重金招聘Python工程師標準>>> 在Ubuntu系統中因為一些原因我使用如下命令修改了/usr目錄的擁有者權限&#xff1a; chown -R root:root /usr結果直接導致系統無法正常啟動&#xff0c;通過跟蹤系統啟動日志/var/log/syslog找到如下失敗原因&#xff1…

[轉載]unix環境高級編程備忘:理解保存的設置用戶ID,設置用戶ID位,有效用戶ID,實際用戶ID...

轉載自http://www.cnblogs.com/stemon/p/5287631.html 一、基本概念 實際用戶ID(RUID)&#xff1a;用于標識一個系統中用戶是誰&#xff0c;一般是在登錄之后&#xff0c;就被唯一的確定&#xff0c;就是登錄的用戶的uid。 有效用戶ID(EUID)&#xff1a;用于系統決定用戶對系統…