「私はあなたをフォローしています」またはCADaからSCADAを作成する方法(MultiCAD.NET API)

私はこの記事が単に「キャッチーな有望な」タイトルを持っていることを正直に認めなければなりません:)

しかし、実際 、今週は自由時間がほとんどなかったため、今回はメモが短く 、非常に概念的なものになります。 したがって、本当のSCADAは当然来ません。



前回の記事では、MultiCAD.NET API(プログラムの有料版と無料版の両方)を使用して、国内のNanoCAD CADシステムでカスタムオブジェクトを作成する方法を考えました。今回は、以前に開発したオブジェクト(ドア)を使用して、テキストの状態追跡する方法を教えますファイル



なぜこれが必要なのですか? 猫の下でお願いします。









内容:



パートI:はじめに。

パートII:NanoCAD 8.5のコードの記述

パートIII:無料のNanoCAD 5.1に適応しようとしています。

パートIV:結論



最初は、NanoCADを単純なSCADAシステムのベースとして使用するというアイデアの大部分はDRZugrikが所有していることを認めていますが、ついに確認することができました。 誰の期待をも混乱させないために、もう一度繰り返します。この記事では、ベールを少しだけ開けて、NanoCADを使用するという少し標準的でない概念を検討します。



MultiCAD.NET APIの選択は私の新しい趣味なので、引き続き作業を続けます。 このAPIにまったく精通していない場合は、このトピックに関する最初の記事をご覧ください。

さて、 2番目の記事の開発、特に擬似3次元ドアを使用します。



NanoCADでカスタムプリミティブを作成することを最後に学習した結果、壁とドアを作成した結果、ドアは擬似3次元オブジェクト(ソリッドプロパティのない線のみ)として作成され、プロパティで選択した値に応じて開閉できました。 時間を節約し、新しいものを発明しないために、それを使用します。



「テキストファイルからデータを読み取る必要があるのはなぜですか」という質問に戻りましょう。 私は答えます-ファイルからの読み取りは、何らかの形で機器の操作のシミュレーションになります。 理論的には、Webサーバーからデータを読み取ったり、何らかの方法で実際のハードウェアからの読み取りを調整したりすることは可能ですが、最も単純な概念に焦点を当てます。



以下、ネタバレの下で、 NanoCAD 8.5の更新されたクラスの完全なコードが表示されます 。このコードはGitHubでも利用できます。MSVisual StudioでNanoCAD APIのプロジェクトを作成する方法を突然忘れた場合は、 NC 8.5およびNC 5.1 Free



念のため、プログラマはなくあなたに警告し、NanoCAD APIを習得し始めたばかりなので、何かのコードや実装曲線にエラーがあるかもしれません



オブジェクトをコピーして監視機能を再割り当てしようとすると、重大なエラーの1つが発生することがあります(ただし、以前のバージョンではコピーエラーが発生しました)。 経験豊富な人が理由を教えてくれたら、感謝します。



また、各ドアに固有のファイルがある場合にのみ、ドアが正常に機能することを警告します。そうしないと、障害が発生する可能性があります。



NC 8.5の完全なドアコード
// Version 1.1 //Use Microsoft .NET Framework 4 and MultiCad.NET API 7.0 //Class for demonstrating the capabilities of MultiCad.NET //Assembly for the Nanocad 8.5 SDK is recommended (however, it is may be possible in the all 8. family) //Link imapimgd, mapimgd.dll and mapibasetypes.dll from SDK //Link System.Windows.Forms and System.Drawing //The commands: draws a pseudo 3D door. //This code in the part of non-infringing rights Nanosoft can be used and distributed in any accessible ways. //For the consequences of the code application, the developer is not responsible. // V 1.0. More detailed - https://habrahabr.ru/post/342680/ // V 1.1. More detailed - https://habrahabr.ru/post/343772/ // PS A big thanks to Alexander Vologodsky for help in developing a method for pivoting object. using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; //added in V 1.1. for monitoring using System.Security.Permissions; using System.IO; using Multicad.AplicationServices; namespace nanodoor2 { //change "8b0986c0-4163-42a4-b005-187111b499d7" for your Guid from Assembly. // Be careful GUID for door and wall classes must be different! // Otherwise there will be problems with saving and moving [CustomEntity(typeof(DoorPseudo3D), "8b0986c0-4163-42a4-b005-187111b499d9", "DoorPseudo3D", "DoorPseudo3D Sample Entity")] [Serializable] public class DoorPseudo3D : McCustomBase { // First and second vertices of the box private Point3d _pnt1 = new Point3d(0, 0, 0); private double _h = 2085; private Vector3d _vecStraightDirection = new Vector3d(1, 0, 0); private Vector3d _vecDirectionClosed = new Vector3d(1, 0, 0); public enum Status {closed , middle, open }; private Status _dStatus = Status.closed; //added in V 1.1. (monitor fileds) private bool _monitor = false; private string _monFilePath = @"E:\test.txt"; // if it's Serialized you can't copy the object in CAD editor [NonSerialized] private FileSystemWatcher _watcher ; [NonSerialized] private FileSystemEventHandler _watchHandler; [CommandMethod("DrawDoor", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawDoor() { DoorPseudo3D door = new DoorPseudo3D(); door.PlaceObject(); } public override void OnDraw(GeometryBuilder dc) { dc.Clear(); // Define the basic points for drawing Point3d pnt1 = new Point3d(0, 0, 0); Point3d pnt2 = new Point3d(pnt1.X + 984, pnt1.Y, 0); Point3d pnt3 = new Point3d(pnt2.X + 0, pnt1.Y+50, 0); Point3d pnt4 = new Point3d(pnt1.X , pnt3.Y, 0); // Set the color to ByObject value dc.Color = McDbEntity.ByObject; Vector3d hvec = new Vector3d(0, 0, _h); // Draw the upper and lower sides dc.DrawPolyline(new Point3d[] { pnt1, pnt2, pnt3, pnt4, pnt1 }); dc.DrawPolyline(new Point3d[] { pnt1.Add(hvec), pnt2.Add(hvec), pnt3.Add(hvec), pnt4.Add(hvec), pnt1.Add(hvec)}); // Draw the edges dc.DrawLine(pnt1, pnt1.Add(hvec)); dc.DrawLine(pnt2, pnt2.Add(hvec)); dc.DrawLine(pnt3, pnt3.Add(hvec)); dc.DrawLine(pnt4, pnt4.Add(hvec)); // Drawing a Door Handle dc.DrawLine(pnt2.Add(new Vector3d( - 190, -0, _h*0.45)), pnt2.Add(new Vector3d(-100, 0, _h * 0.45))); dc.DrawLine(pnt3.Add(new Vector3d(-190, 0, _h * 0.45)), pnt3.Add(new Vector3d(-100, 0, _h * 0.45))); // Create contours for the front and rear sides and hatch them // In this demo, we hatch only two sides, you can tailor the others yourself List<Polyline3d> c1 = new List<Polyline3d>(); c1.Add(new Polyline3d( new List<Point3d>() { pnt1, pnt1.Add(hvec), pnt2.Add(hvec), pnt2, pnt1, })); List<Polyline3d> c2 = new List<Polyline3d>(); c2.Add(new Polyline3d( new List<Point3d>() { pnt4, pnt4.Add(hvec), pnt3.Add(hvec), pnt3, pnt4, })); dc.DrawGeometry(new Hatch(c1, "JIS_WOOD", 0, 170, false, HatchStyle.Normal, PatternType.PreDefined, 500), 1); dc.DrawGeometry(new Hatch(c2, "JIS_WOOD", 0, 170, false, HatchStyle.Normal, PatternType.PreDefined, 500), 1); } public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select first point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _pnt1 = res.Point; // Add the object to the database DbEntity.AddToCurrentDocument(); return hresult.s_Ok; } /// <summary> /// Method for changing the object's SC (the graph is built at the origin of coordinates). /// </ summary> /// <param name = "tfm"> The matrix for changing the position of the object. </ param> /// <returns> True - if the matrix is passed, False - if not. </ returns> public override bool GetECS(out Matrix3d tfm) { // Create a matrix that transforms the object. // The object is drawn in coordinates(0.0), then it is transformed with the help of this matrix. tfm = Matrix3d.Displacement(this._pnt1.GetAsVector()) * Matrix3d.Rotation (-this._vecStraightDirection.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis), Vector3d.ZAxis, Point3d.Origin); return true; } public override void OnTransform(Matrix3d tfm) { // To be able to cancel(Undo) McUndoPoint undo = new McUndoPoint(); undo.Start(); // Get the coordinates of the base point and the rotation vector this.TryModify(); this._pnt1 = this._pnt1.TransformBy(tfm); this.TryModify(); this._vecStraightDirection = this._vecStraightDirection.TransformBy(tfm); // We move the door only when it is closed if not - undo if (_dStatus == Status.closed) _vecDirectionClosed = _vecStraightDirection; else { MessageBox.Show("Please transform only closed door"); undo.Undo(); } undo.Stop(); } //Define the custom properties of the object [DisplayName("Height")] [Description("Height of door")] [Category("Door options")] public double HDoor { get { return _h; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _h = value; } } [DisplayName("Door status")] [Description("Door may be: closed, middle, open")] [Category("Door options")] public Status Stat { get { return _dStatus; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; // Change the rotation vector for each of the door states switch (value) { case Status.closed: _vecStraightDirection = _vecDirectionClosed; break; case Status.middle: _vecStraightDirection = _vecDirectionClosed.Add(_vecDirectionClosed.GetPerpendicularVector().Negate() * 0.575) ; break; case Status.open: _vecStraightDirection = _vecDirectionClosed.GetPerpendicularVector()*-1; break; default: _vecStraightDirection = _vecDirectionClosed; break; } _dStatus = value; } } // Create a grip for the base point of the object public override bool GetGripPoints(GripPointsInfo info) { info.AppendGrip(new McSmartGrip<DoorPseudo3D>(_pnt1, (obj, g, offset) => { obj.TryModify(); obj._pnt1 += offset; })); return true; } //Define the monitoring custom properties , added v. 1.1: // added in v. 1.1 [DisplayName("Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public bool Monitor { get { return _monitor; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monitor = value; if (_monitor) { StartMonitoring(); } else StopMonitoring(); } } // added in v. 1.1 [DisplayName("File path for Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public string MonitoringFilPath { get { return _monFilePath; } set { //for hot change filename if (Monitor) { StopMonitoring(); if (!TryModify()) return; _monFilePath = value; StartMonitoring(); McContext.ShowNotification("Monitored file is changed"); } else { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monFilePath = value; } } } //Define the methods, added v. 1.1: // added in v. 1.1 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public void StartMonitoring() { _watcher = new FileSystemWatcher(); if (File.Exists(_monFilePath)) { _watcher.Path = Path.GetDirectoryName(_monFilePath); _watcher.Filter = Path.GetFileName(_monFilePath); _watchHandler = new FileSystemEventHandler(OnChanged); _watcher.Changed += _watchHandler; _watcher.EnableRaisingEvents = true; } else McContext.ShowNotification("File: " + _monFilePath + " " + "not Exists"); } // added in v. 1.1 public void StopMonitoring() { if (_watcher != null & _watchHandler != null) { _watcher.Changed -= _watchHandler; _watcher.EnableRaisingEvents = false; } } // added in v. 1.1 private void OnChanged(object source, FileSystemEventArgs e) { McContext.ShowNotification("File: " + e.FullPath + " " + e.ChangeType); //read new value from file try { if (File.Exists(_monFilePath)) { int mStatus = -1; McContext.ShowNotification("File exists "); using (StreamReader sr = new StreamReader(new FileStream(_monFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { if (sr.BaseStream.CanRead) { McContext.ShowNotification("can read "); if (int.TryParse(sr.ReadLine(), out mStatus)) { McContext.ShowNotification("parse correct "); if (Enum.IsDefined(typeof(Status), mStatus)) { if (!TryModify()) return; Stat = (Status) mStatus; if (!TryModify()) return; if (!DbEntity.Update()) return; McContext.ShowNotification("Door state is changed"); McContext.ExecuteCommand("REGENALL"); } else McContext.ShowNotification("Incorrect data in the file. Should be in diapason: 0, 1, 2 "); } } else McContext.ShowNotification("Can't read file "); } } else McContext.ShowNotification("File not exists "); _watcher.EnableRaisingEvents = false; // disable tracking } finally { _watcher.EnableRaisingEvents = true; // reconnect tracking } } } // TODO: There are many shortcomings in this code. // Including failures when working with copying, moving objects and saving files, you can improve it if you want. }
      
      







いくつかの新しいポイントを簡単に分析します(以前の記事と比較して)。



 //added in V 1.1. for monitoring using System.Security.Permissions; using System.IO; using Multicad.AplicationServices;
      
      





新しい名前空間。 これらは、ファイルの監視、ファイルからの読み取り、およびプログラムコンソールでのメッセージの表示に必要です。



 //added in V 1.1. for monitoring //added in V 1.1. (monitor fileds) private bool _monitor = false; private string _monFilePath = @"E:\test.txt"; FileSystemEventHandler _watchHandler; FileSystemWatcher _watcher;
      
      





ドアの状態を追跡できるプロパティの新しいタイプとクラスフィールド。



  //Define the monitoring custom properties , added v. 1.1: // added in v. 1.1 [DisplayName("Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public bool Monitor { get { return _monitor; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monitor = value; if (_monitor) { StartMonitoring(); } else StopMonitoring(); } } // added in v. 1.1 [DisplayName("File path for Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public string MonitoringFilPath { get { return _monFilePath; } set { //for hot change filename if (Monitor) { StopMonitoring(); if (!TryModify()) return; _monFilePath = value; StartMonitoring(); McContext.ShowNotification("Monitored file is changed"); } else { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monFilePath = value; } } }
      
      







最初のプロパティは、ファイル追跡モードの有効化と無効化を担当します。

注意! このオプションをオンにしたとき、ファイルからのデータの読み取りは実装しませんでした。オブジェクトの実際の同期は、読み取りファイルの最初の変更後に始まります。



2番目のプロパティには、宛先ファイルへのアドレスが含まれています(1つのオブジェクトに対して1つのファイルを追跡しています。さらに、一意であるか、失敗します)。 ファイル監視が有効になっている場合、このプロパティを変更すると、監視は自動的に新しいファイルに切り替わります。



さらに行こう



 //Define the methods, added v. 1.1: // added in v. 1.1 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public void StartMonitoring() { _watcher = new FileSystemWatcher(); if (File.Exists(_monFilePath)) { _watcher.Path = Path.GetDirectoryName(_monFilePath); _watcher.Filter = Path.GetFileName(_monFilePath); _watchHandler = new FileSystemEventHandler(OnChanged); _watcher.Changed += _watchHandler; _watcher.EnableRaisingEvents = true; } else McContext.ShowNotification("File: " + _monFilePath + " " + "not Exists"); } // added in v. 1.1 public void StopMonitoring() { if (_watcher != null & _watchHandler != null) { _watcher.Changed -= _watchHandler; _watcher.EnableRaisingEvents = false; } }
      
      





最初の方法では、通常の.NETライブラリを使用して、ファイルの変更に関するシステム情報を購読できます。



2番目の方法-トラッキングの登録を解除します。

[PermissionSet(SecurityAction.Demand、Name =“ FullTrust”)]は、ほとんどのメソッドコードと同様に、Microsoftのから取られています。 触れないことにした。



更新されたコードの最後のビットは残ります。



 // added in v. 1.1 private void OnChanged(object source, FileSystemEventArgs e) { McContext.ShowNotification("File: " + e.FullPath + " " + e.ChangeType); //read new value from file try { if (File.Exists(_monFilePath)) { int mStatus = -1; McContext.ShowNotification("File exists "); using (StreamReader sr = new StreamReader(new FileStream(_monFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { if (sr.BaseStream.CanRead) { if (int.TryParse(sr.ReadLine(), out mStatus)) { if (Enum.IsDefined(typeof(Status), mStatus)) { if (!this.TryModify()) return; this.Stat = (Status)mStatus; if (!TryModify()) return; this.DbEntity.Update(); McContext.ExecuteCommand("REGENALL"); McContext.ShowNotification("Door state is changed"); } else McContext.ShowNotification("Incorrect data in the file. Should be in diapason: 0, 1, 2 "); } } else McContext.ShowNotification("Can't read file "); } } else McContext.ShowNotification("File not exists "); _watcher.EnableRaisingEvents = false; // disable tracking } finally { _watcher.EnableRaisingEvents = true; // reconnect tracking } }
      
      





イベントに応答するためのメソッド。 最初にファイルが存在するかどうかを確認し、ファイルから最初の行を抽出して、列挙値(0から2の範囲)と一致する場合、ドアの状態を制御するプロパティを変更し、標準の画面更新コマンドを実行します。変更は常に表示されるとは限りません。





Nanocad 5.1の無料版では、コードはほとんど同じです。



NC 5.1のドアの完全なコード
 // Version 1.1 //Use Microsoft .NET Framework 3.5 and old version of MultiCad.NET (for NC 5.1) //Class for demonstrating the capabilities of MultiCad.NET //Assembly for the Nanocad 5.1 //Link mapimgd.dll from Nanocad SDK //Link System.Windows.Forms and System.Drawing //upd: for version 1.1 also link .NET API: hostdbmg.dll, hostmgd.dll //The commands: draws a pseudo 3D door. //This code in the part of non-infringing rights Nanosoft can be used and distributed in any accessible ways. //For the consequences of the code application, the developer is not responsible. // V 1.0. More detailed - https://habrahabr.ru/post/342680/ // V 1.1. More detailed - https://habrahabr.ru/post/343772/ // PS A big thanks to Alexander Vologodsky for help in developing a method for pivoting object. using System; using System.ComponentModel; using System.Windows.Forms; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; //added in V 1.1. for monitoring using System.Security.Permissions; using System.IO; using Multicad.AplicationServices; using HostMgd.ApplicationServices; using HostMgd.EditorInput; namespace nanodoor2 { //change "8b0986c0-4163-42a4-b005-187111b499d7" for your Guid from Assembly. // Be careful GUID for door and wall classes must be different! // Otherwise there will be problems with saving and moving [CustomEntity(typeof(DoorPseudo3D_nc51), "b4edac1f-7978-483f-91b1-10503d20735b", "DoorPseudo3D_nc51", "DoorPseudo3D_nc51 Sample Entity")] [Serializable] public class DoorPseudo3D_nc51 : McCustomBase { // First and second vertices of the box private Point3d _pnt1 = new Point3d(0, 0, 0); private double _scale = 1000; private double _h = 2085; private Vector3d _vecStraightDirection = new Vector3d(1, 0, 0); private Vector3d _vecDirectionClosed = new Vector3d(1, 0, 0); public enum Status {closed , middle, open}; private Status _dStatus = Status.closed; //added in V 1.1. (monitor fileds) public enum Mon { off, on}; private Mon _monitor = Mon.off; private string _monFilePath = @"E:\test.txt"; // if it is serialized, you may not be able to copy the object in the CAD editor [NonSerialized] private FileSystemWatcher _watcher; [NonSerialized] private FileSystemEventHandler _watchHandler; [CommandMethod("DrawDoor", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawDoor() { DoorPseudo3D_nc51 door = new DoorPseudo3D_nc51(); door.PlaceObject(); this.TryModify(); // this.Monitor = false; } public override void OnDraw(GeometryBuilder dc) { dc.Clear(); // Define the basic points for drawing Point3d pnt1 = new Point3d(0, 0, 0); Point3d pnt2 = new Point3d(pnt1.X + (984 * _scale), pnt1.Y, 0); Point3d pnt3 = new Point3d(pnt2.X + 0, pnt1.Y+(50 * _scale), 0); Point3d pnt4 = new Point3d(pnt1.X , pnt3.Y, 0) ; // Set the color to ByObject value dc.Color = McDbEntity.ByObject; Vector3d hvec = new Vector3d(0, 0, _h * _scale) ; // Draw the upper and lower sides dc.DrawPolyline(new Point3d[] { pnt1, pnt2, pnt3, pnt4, pnt1 }); dc.DrawPolyline(new Point3d[] { pnt1.Add(hvec), pnt2.Add(hvec), pnt3.Add(hvec), pnt4.Add(hvec), pnt1.Add(hvec)}); // Draw the edges dc.DrawLine(pnt1, pnt1.Add(hvec)); dc.DrawLine(pnt2, pnt2.Add(hvec)); dc.DrawLine(pnt3, pnt3.Add(hvec)); dc.DrawLine(pnt4, pnt4.Add(hvec)); // Drawing a Door Handle dc.DrawLine(pnt2.Add(new Vector3d( -190 * _scale, -0, _h*0.45 * _scale)), pnt2.Add(new Vector3d(-100 * _scale, 0, _h * 0.45 * _scale))); dc.DrawLine(pnt3.Add(new Vector3d(-190 * _scale, 0, _h * 0.45 * _scale)), pnt3.Add(new Vector3d(-100 * _scale, 0, _h * 0.45 * _scale))); } public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select first point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _pnt1 = res.Point; Stat = Status.closed; // Add the object to the database DbEntity.AddToCurrentDocument(); // added in v.1. return hresult.s_Ok; } /// <summary> /// Method for changing the object's SC (the graph is built at the origin of coordinates). /// </ summary> /// <param name = "tfm"> The matrix for changing the position of the object. </ param> /// <returns> True - if the matrix is passed, False - if not. </ returns> public override bool GetECS(out Matrix3d tfm) { // Create a matrix that transforms the object. // The object is drawn in coordinates(0.0), then it is transformed with the help of this matrix. tfm = Matrix3d.Displacement(this._pnt1.GetAsVector()) * Matrix3d.Rotation (-this._vecStraightDirection.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis), Vector3d.ZAxis, Point3d.Origin); return true; } public override void OnTransform(Matrix3d tfm) { // To be able to cancel(Undo) McUndoPoint undo = new McUndoPoint(); undo.Start(); // Get the coordinates of the base point and the rotation vector this.TryModify(); this._pnt1 = this._pnt1.TransformBy(tfm); this.TryModify(); this._vecStraightDirection = this._vecStraightDirection.TransformBy(tfm); // We move the door only when it is closed if not - undo if (_dStatus == Status.closed) _vecDirectionClosed = _vecStraightDirection; else { MessageBox.Show("Please transform only closed door (when its status = 0)"); undo.Undo(); } undo.Stop(); } //Define the custom properties of the object [DisplayName("Height")] [Description("Height of door")] [Category("Door options")] public double HDoor { get { return _h; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _h = value; } } [DisplayName("DScale")] [Description("Door Scale")] [Category("Door options")] public double DScale { get { return _scale; } set { if (!TryModify()) return; _scale = value; } } [DisplayName("Door status")] [Description("0-closed, 1-midle, 2-open")] [Category("Door options")] public Status Stat { get { return _dStatus; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; // Change the rotation vector for each of the door states switch (value) { case Status.closed: _vecStraightDirection = _vecDirectionClosed; break; case Status.middle: _vecStraightDirection = _vecDirectionClosed.Add(_vecDirectionClosed.GetPerpendicularVector().Negate() * 0.575) ; break; case Status.open: _vecStraightDirection = _vecDirectionClosed.GetPerpendicularVector()*-1; break; default: break; } _dStatus = value; } } // Create a grip for the base point of the object public override bool GetGripPoints(GripPointsInfo info) { info.AppendGrip(new McSmartGrip<DoorPseudo3D_nc51>(_pnt1, (obj, g, offset) => { obj.TryModify(); obj._pnt1 += offset; })); return true; } //Define the monitoring custom properties , added v. 1.1: // added in v. 1.1 [DisplayName("Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public Mon Monitor { get { return _monitor; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monitor = value; if (_monitor==Mon.on) { StartMonitoring(); } else StopMonitoring(); // if (_monitor) // { // StartMonitoring(); // }// Get the command line editor // else StopMonitoring(); } } // added in v. 1.1 [DisplayName("File path for Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public string MonitoringFilPath { get { return _monFilePath; } set { // Get the command line editor DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; //for hot change filename if (Monitor==Mon.on) { StopMonitoring(); if (!TryModify()) return; _monFilePath = value; StartMonitoring(); ed.WriteMessage("Monitored file is changed"); } else { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monFilePath = value; } } } //Define the methods, added v. 1.1: // added in v. 1.1 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public void StartMonitoring() { DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; _watcher = new FileSystemWatcher(); if (File.Exists(_monFilePath)) { _watcher.Path = Path.GetDirectoryName(_monFilePath); _watcher.Filter = Path.GetFileName(_monFilePath); _watchHandler = new FileSystemEventHandler(OnChanged); _watcher.Changed += _watchHandler; _watcher.EnableRaisingEvents = true; } else ed.WriteMessage("File: " + _monFilePath + " " + "not Exists"); } // added in v. 1.1 public void StopMonitoring() { if (_watcher != null & _watchHandler != null) { _watcher.Changed -= _watchHandler; _watcher.EnableRaisingEvents = false; } } // added in v. 1.1 private void OnChanged(object source, FileSystemEventArgs e) { DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; ed.WriteMessage("File: " + e.FullPath + " " + e.ChangeType); //read new value from file try { if (File.Exists(_monFilePath)) { int mStatus = -1; ed.WriteMessage("File exists "); using (StreamReader sr = new StreamReader(new FileStream(_monFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { if (sr.BaseStream.CanRead) { if (int.TryParse(sr.ReadLine(), out mStatus)) { if (Enum.IsDefined(typeof(Status), mStatus)) { if (!TryModify()) return; Stat = (Status)mStatus; //  if (!TryModify()) return; DbEntity.Update(); McContext.ExecuteCommand("REGENALL"); ed.WriteMessage("Door state is changed"); } else ed.WriteMessage("Incorrect data in the file. Should be in diapason: 0, 1, 2 "); } } else ed.WriteMessage("Can't read file "); } } else ed.WriteMessage("File not exists "); _watcher.EnableRaisingEvents = false; // disable tracking } finally { _watcher.EnableRaisingEvents = true; // reconnect tracking } } } // TODO: There are many shortcomings in this code. // Including failures when working with copying, moving objects and saving files, you can improve it if you want. }
      
      







違いは主に、 McContext.ShowNotification()がMultiCAD.NET APIの古いバージョンにまだ実装されていないという事実にあるため、通常の.NET API(hostdbmg.dllおよびhostmgd.dllライブラリ)を接続します。 次に、このメソッドを束に置き換えます。



  DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; ed.WriteMessage(" ...");
      
      







最初の2行では入力コンソールにアクセスでき、最後の1行でもテキストメッセージが表示されます。



その結果、写真にあるものを取得します。







バージョンNC 8.5の上の図、NC 5.1の下のオブジェクトは、疑似3次元と2次元の両方の形式で表示されます。





まとめると。



ここで検討する例はひどく失敗しますが、一方でサーバーとの相互作用を考慮していませんが、この単純な例は単純にSCADAのプラットフォームとしてのNanoCADの可能性を概念的に示しています。



さらに多くのことができ、オブジェクトへの変更を確認するためのダイアログを追加したり、ハードウェアを操作したり、アクセスレベルを区切ったりすることができます。 ユーザー環境をカスタマイズする微妙な可能性があるため、理論的には、プラットフォームをSCADAシステムを表示するエディターとして、およびユーザー環境として使用できます。つまり、すべての描画ツールを削除し、監視および制御用のウィンドウのみを残します。



NanoCADに基づいてWonderware InTouchをライバルにすることを強くお勧めするわけではありませんが、5年前にBolidから簡単なSCADA "アルゴリズム"があったことを覚えています(実際に存在します。同様の類似物(5年前のバージョン)は、NanoCADプラットフォームに基づく機能性の観点から記述できます。



おそらく、NanoCADaに基づいたSCADAテーマに戻りますが、今のところは建設的なコメントを読んで喜んでいるでしょう



All Articles