파일정리

This commit is contained in:
ChiKyun Kim
2026-01-29 14:03:17 +09:00
parent 00cc0ef5b7
commit 58ca67150d
440 changed files with 47236 additions and 99165 deletions

View File

@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "agvControl", "agvControl.csproj", "{EB23DC0D-9A07-407C-762D-DF1CC8B3D822}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EB23DC0D-9A07-407C-762D-DF1CC8B3D822}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB23DC0D-9A07-407C-762D-DF1CC8B3D822}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB23DC0D-9A07-407C-762D-DF1CC8B3D822}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB23DC0D-9A07-407C-762D-DF1CC8B3D822}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {586342A4-FEA6-4584-82F4-39BA06630ABB}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,37 @@
namespace AGVControl
{
partial class BatteryLevelGauge
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AGVControl
{
public partial class BatteryLevelGauge : Control
{
float _vlevel = 50;
public float VLevel { get { return _vlevel; } set { _vlevel = value; } }
public float Volt { get; set; } = 0;
public String sign { get; set; } = "%";
public float CurA { get; set; } = 0;
public float MaxA { get; set; } = 0;
bool isopen = false;
public Boolean IsOpen { get { return isopen; } set { isopen = value; this.Invalidate(); } }
public Color BorderColor { get; set; } = Color.DimGray;
public BatteryLevelGauge()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.Resize += arLabel_Resize;
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.InterpolationMode = InterpolationMode.High;
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
//base.OnPaint(pe);
pe.Graphics.FillRectangle(new SolidBrush(BackColor), DisplayRectangle);
var r = new RectangleF(this.DisplayRectangle.Left + Padding.Left,
this.DisplayRectangle.Top + Padding.Top,
this.DisplayRectangle.Width - Padding.Left - Padding.Right,
this.DisplayRectangle.Height - Padding.Top - Padding.Bottom);
pe.Graphics.FillRectangle(new SolidBrush(this.BackColor), r);
var w = r.Width * (this.VLevel / 100f);
var lr = new RectangleF(r.Left, r.Top, w, r.Height);
Color bColor = Color.Red;
if (VLevel > 80) bColor = Color.YellowGreen;
else if (VLevel > 60) bColor = Color.Yellow;
else if (VLevel > 40) bColor = Color.Orange;
else if (VLevel > 20) bColor = Color.Tomato;
else bColor = Color.Red;
pe.Graphics.FillRectangle(new SolidBrush(bColor), lr);
Color textcolor = this.ForeColor;
if (IsOpen == false) textcolor = Color.Black;
var smg = IsOpen ? $"{ this.VLevel:N0}{ this.sign}" : "연결안됨";
pe.Graphics.DrawString(smg, this.Font, new SolidBrush(textcolor), r,
new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
});
pe.Graphics.DrawRectangle(Pens.Black, r.Left, r.Top, r.Width, r.Height);
}
void arLabel_Resize(object sender, EventArgs e)
{
Invalidate();
}
}
}

View File

@@ -0,0 +1,103 @@
namespace AGVControl.Dialog
{
partial class fMapDesign
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fMapDesign));
this.panel1 = new System.Windows.Forms.Panel();
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.btOK = new System.Windows.Forms.ToolStripButton();
this.toolStrip1.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold);
this.panel1.Location = new System.Drawing.Point(0, 47);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(888, 547);
this.panel1.TabIndex = 22;
//
// toolStrip1
//
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(40, 40);
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.btOK});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(888, 47);
this.toolStrip1.TabIndex = 23;
this.toolStrip1.Text = "toolStrip1";
//
// statusStrip1
//
this.statusStrip1.Location = new System.Drawing.Point(0, 594);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(888, 22);
this.statusStrip1.TabIndex = 24;
this.statusStrip1.Text = "statusStrip1";
//
// btOK
//
this.btOK.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.btOK.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.btOK.Image = ((System.Drawing.Image)(resources.GetObject("btOK.Image")));
this.btOK.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btOK.Name = "btOK";
this.btOK.Size = new System.Drawing.Size(44, 44);
this.btOK.Text = "toolStripButton1";
this.btOK.Click += new System.EventHandler(this.btOK_Click);
//
// fMapDesign
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(888, 616);
this.Controls.Add(this.panel1);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.toolStrip1);
this.Name = "fMapDesign";
this.Text = "fMapDesign";
this.Load += new System.EventHandler(this.fMapDesign_Load);
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripButton btOK;
private System.Windows.Forms.StatusStrip statusStrip1;
}
}

View File

@@ -0,0 +1,48 @@
using AR;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AGVControl.Dialog
{
public partial class fMapDesign : Form
{
AGVControl.MapControl mapctl;
public fMapDesign(string fn)
{
InitializeComponent();
mapctl = new AGVControl.MapControl();
mapctl.Dock = DockStyle.Fill;
mapctl.Visible = true;
mapctl.Font = this.panel1.Font;
mapctl.BackColor = Color.FromArgb(32, 32, 32);
this.panel1.Controls.Add(mapctl);
if (System.IO.File.Exists(fn))
{
//auto load
var rlt = mapctl.LoadFromFile(fn, out string errmsg);
if (rlt == false) AR.UTIL.MsgE(errmsg);
}
}
private void fMapDesign_Load(object sender, EventArgs e)
{
}
private void btOK_Click(object sender, EventArgs e)
{
this.mapctl.SaveToFile(this.mapctl.Filename);
DialogResult = DialogResult.OK;
}
}
}

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="btOK.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
</value>
</data>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>124, 17</value>
</metadata>
</root>

View File

@@ -0,0 +1,158 @@
namespace AGVControl.Dialog
{
partial class fPropertyRFIDPoint
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.propertyGrid2 = new System.Windows.Forms.PropertyGrid();
this.panel1 = new System.Windows.Forms.Panel();
this.label2 = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.SuspendLayout();
//
// propertyGrid1
//
this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid1.Location = new System.Drawing.Point(0, 31);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.Size = new System.Drawing.Size(346, 456);
this.propertyGrid1.TabIndex = 0;
//
// comboBox1
//
this.comboBox1.Dock = System.Windows.Forms.DockStyle.Top;
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBox1.Font = new System.Drawing.Font("굴림", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(1, 32);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(519, 32);
this.comboBox1.TabIndex = 1;
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
//
// propertyGrid2
//
this.propertyGrid2.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid2.Location = new System.Drawing.Point(1, 64);
this.propertyGrid2.Name = "propertyGrid2";
this.propertyGrid2.Size = new System.Drawing.Size(519, 362);
this.propertyGrid2.TabIndex = 2;
//
// panel1
//
this.panel1.BackColor = System.Drawing.Color.Gray;
this.panel1.Controls.Add(this.propertyGrid2);
this.panel1.Controls.Add(this.button1);
this.panel1.Controls.Add(this.comboBox1);
this.panel1.Controls.Add(this.label2);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(346, 0);
this.panel1.Name = "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(1);
this.panel1.Size = new System.Drawing.Size(521, 487);
this.panel1.TabIndex = 3;
//
// label2
//
this.label2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.label2.Dock = System.Windows.Forms.DockStyle.Top;
this.label2.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label2.Location = new System.Drawing.Point(1, 1);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(519, 31);
this.label2.TabIndex = 3;
this.label2.Text = "Connection Information";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// panel2
//
this.panel2.Controls.Add(this.propertyGrid1);
this.panel2.Controls.Add(this.label1);
this.panel2.Dock = System.Windows.Forms.DockStyle.Left;
this.panel2.Location = new System.Drawing.Point(0, 0);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(346, 487);
this.panel2.TabIndex = 4;
//
// label1
//
this.label1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.label1.Dock = System.Windows.Forms.DockStyle.Top;
this.label1.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.Location = new System.Drawing.Point(0, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(346, 31);
this.label1.TabIndex = 0;
this.label1.Text = "Point Information";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// button1
//
this.button1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.button1.Font = new System.Drawing.Font("Arial Rounded MT Bold", 27.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.Location = new System.Drawing.Point(1, 426);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(519, 60);
this.button1.TabIndex = 4;
this.button1.Text = "P1 ↔ P2";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// fPropertyRFIDPoint
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(867, 487);
this.Controls.Add(this.panel1);
this.Controls.Add(this.panel2);
this.Name = "fPropertyRFIDPoint";
this.Text = "fPropertyRFIDPoint";
this.Load += new System.EventHandler(this.fPropertyRFIDPoint_Load);
this.panel1.ResumeLayout(false);
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PropertyGrid propertyGrid1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.PropertyGrid propertyGrid2;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -0,0 +1,55 @@
using AGVControl.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AGVControl.Dialog
{
public partial class fPropertyRFIDPoint : Form
{
RFIDPoint RFIDPt;
List<RFIDConnection> Connections;
public fPropertyRFIDPoint(RFIDPoint point, List<RFIDConnection> connection)
{
InitializeComponent();
this.RFIDPt = point;
this.Connections = connection;
this.propertyGrid1.SelectedObject = point;
this.KeyPreview = true;
this.KeyDown += (s1, e1) => {
if (e1.KeyCode == Keys.Escape) this.Close();
};
}
private void fPropertyRFIDPoint_Load(object sender, EventArgs e)
{
foreach (var item in Connections)
comboBox1.Items.Add($"{item.P1.Value} ↔ {item.P2.Value}");
if(comboBox1.Items.Count > 0)
comboBox1.SelectedIndex = 0;
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
this.propertyGrid2.SelectedObject = this.Connections[this.comboBox1.SelectedIndex];
}
private void button1_Click(object sender, EventArgs e)
{
var item = this.Connections[this.comboBox1.SelectedIndex];
var p1 = item.P1;
var p2 = item.P2;
item.P2 = p1;// item.P1;
item.P1 = p2;// p1;
this.propertyGrid2.SelectedObject = item;
this.Validate();
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace arFrame.Control
{
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public class ColorListItem
{
public System.Drawing.Color BackColor1 { get; set; }
public System.Drawing.Color BackColor2 { get; set; }
public string Remark { get; set; }
public ColorListItem()
{
BackColor1 = System.Drawing.Color.Transparent;
BackColor2 = System.Drawing.Color.Transparent;
Remark = string.Empty;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace arFrame.Control
{
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public class GridViewItem
{
public int row { get; private set; }
public int col { get; private set; }
public int idx { get; private set; }
[Category("arFrame"), DisplayName("Tag")]
public string Tag { get; set; }
[Category("arFrame"), DisplayName("글자 정렬 방식")]
public System.Drawing.ContentAlignment TextAlign { get; set; }
[Category("arFrame"), DisplayName("글자 여백")]
public System.Windows.Forms.Padding Padding { get; set; }
[Category("arFrame"), DisplayName("메뉴 사용여부"), Description("활성화시 메뉴의 클릭이벤트가 발생하지 않습니다")]
public Boolean Enable { get; set; }
public System.Drawing.Color BackColor1 { get; set; }
public System.Drawing.Color BackColor2 { get; set; }
public Boolean Dirty { get; set; }
public System.Drawing.RectangleF rect { get; set; }
[Category("arFrame"), DisplayName("번호")]
public int No { get; set; }
public GridViewItem(int idx_,int _r,int _c)
{
this.row = _r;
this.col = _c;
BackColor1 = System.Drawing.Color.Transparent;
BackColor2 = System.Drawing.Color.Transparent;
rect = System.Drawing.RectangleF.Empty;
Enable = true;
this.idx = idx_;
TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
Padding = new System.Windows.Forms.Padding(0, 0, 0, 0);
No = 0;
this.Dirty = true;
}
}
}

View File

@@ -0,0 +1,36 @@
namespace arFrame.Control
{
partial class GridView
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마십시오.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,624 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace arFrame.Control
{
public partial class GridView : System.Windows.Forms.Control
{
private int bordersize = 0;
private int menubordersize = 1;
private int menugap = 5;
private System.Windows.Forms.Padding padding = new Padding(3, 3, 3, 3);
private System.Drawing.Color backcolor = System.Drawing.Color.White;
private System.Drawing.Color bordercolor = System.Drawing.Color.Black;
private Boolean textattachtoimage = true;
private Boolean _showIndexString = true;
private System.Drawing.Color _shadowColor = System.Drawing.Color.Transparent;
private System.Drawing.Color foreColorPin = System.Drawing.Color.WhiteSmoke;
private System.Drawing.Font fontPin = new Font("Consolas", 8, FontStyle.Bold);
private Point _matrixsize = new Point(8, 2);
public Point MatrixSize { get { return _matrixsize; } set { _matrixsize = value; ResetArray(); RemakeChartRect(); } }
[Browsable(false)]
public int ColumnCount { get { return _matrixsize.X; } }
[Browsable(false)]
public int RowCount { get { return _matrixsize.Y; } }
public int ItemCount { get { return ColumnCount * RowCount; } }
private GridViewItem[] Items;
private UInt16[] _values;
private string[] _titles;
private string[] _tags;
private string[] _names;
private ColorListItem[] _colorlist;
public ColorListItem[] ColorList { get { return _colorlist; } set { _colorlist = value; this.Invalidate(); } }
public string[] Names { get { return _names; } set { _names = value; Invalidate(); } }
public string[] Titles { get { return _titles; } set { _titles = value; Invalidate(); } }
public UInt16[] Values { get { return _values; } set { _values = value; Invalidate(); } }
public string[] Tags { get { return _tags; } set { _tags = value; } }
private bool _showdebuginfo = false;
public Boolean showDebugInfo { get { return _showdebuginfo; } set { _showdebuginfo = value; Invalidate(); } }
public void setNames(string[] value)
{
_names = value;
}
public void setTitle(string[] value)
{
List<string> titlerows = new List<string>();
List<string> tagrows = new List<string>();
for (int i = 0; i < value.Length; i++)
{
var r = (int)(Math.Floor((double)(i / ColumnCount)));
var c = i % ColumnCount;
if (titlerows.Count < r + 1) titlerows.Add(string.Empty);
if (tagrows.Count < r + 1) tagrows.Add(string.Empty);
var prestr = titlerows[r];
if (prestr != "") prestr += "|";
titlerows[r] = prestr + value[i];
var prestr_t = tagrows[r];
if (prestr_t != "") prestr_t += "|";
tagrows[r] = prestr_t + "";
if (i < itemCount) this.Items[i].Enable = true;
}
this._titles = titlerows.ToArray();
this._tags = tagrows.ToArray();
}
public Boolean setTitle(int row, int col, string value, string itemtag = "")
{
if (_titles == null) _titles = new string[0];
if (_tags == null) _tags = new string[0];
if ( row >= _titles.Length) Array.Resize(ref _titles, row + 1);
if( row >= _tags.Length) Array.Resize(ref _tags, row+1);
if (_titles[row] == null) _titles[row] = string.Empty;
if (_tags[row] == null) _tags[row] = string.Empty;
var linebuf = _titles[row].Split('|');
var linebuf_t = _tags[row].Split('|');
if (col >= linebuf.Length) Array.Resize(ref linebuf, col + 1);
if (col >= linebuf_t.Length) Array.Resize(ref linebuf_t, col + 1);
linebuf[col] = value;
linebuf_t[col] = itemtag;
_titles[row] = string.Join("|", linebuf);
_tags[row] = string.Join("|", linebuf_t);
return true;
//var idx = row * this.ColumnCount + col;
//return setTitle(idx, value);
}
public Boolean setTitle(int idx, string value)
{
if (idx < ColumnCount) return setTitle(0, idx, value);
else
{
//줄값이 필요하다
var row = (int)(Math.Floor((double)(idx / ColumnCount)));
var col = idx % ColumnCount;
return setTitle(row, col, value);
}
}
public void setValue(bool[] value)
{
var v = new UInt16[value.Length];
for (int i = 0; i < value.Length; i++)
v[i] = (UInt16)(value[i] ? 1 : 0);
_values = v;
}
public void setValue(UInt16[] value)
{
_values = value;
}
public Boolean setValue(int idx, ushort value)
{
if (this._values == null) _values = new ushort[idx + 1];
if (idx >= this._values.Length) Array.Resize(ref _values, idx + 1);
this._values[idx] = value;
return true;
}
public Boolean setTag(string[] value)
{
this._tags = value;
return true;
}
public Boolean setTag(int idx, string value)
{
if (this._tags == null) Tags = new string[idx + 1];
if (idx >= this.Tags.Length) Array.Resize(ref _tags, idx + 1);
this._tags[idx] = value;
return true;
}
public void ClearValue(ushort defaultValue = 0)
{
if (_values != null)
for(int i = 0; i < _values.Length;i++)
_values[i] = defaultValue;
}
public void ClearTitle(string defaultValue = "")
{
if (_values != null)
for (int i = 0; i < _titles.Length; i++)
_titles[i] = defaultValue;
}
public void ClearTag(string defaultValue = "")
{
if (_tags != null)
for (int i = 0; i < _tags.Length; i++)
_tags[i] = defaultValue;
}
public void setValue(ushort value)
{
for(int i = 0; i < _values.Length;i++)
this._values[i] = value;
}
public void setItemEnable(int idx, bool value)
{
if (idx >= _values.Length || idx >= this.Items.Length) return;
this.Items[idx].Enable = value;
}
/// <summary>
/// 지정된 컬러태그값을 입력한다.
/// </summary>
/// <param name="idx"></param>
/// <param name="tagString"></param>
/// <returns></returns>
public Boolean setValue(int idx, string tagString)
{
//동일태그값을 찾는다
if (idx >= _values.Length) return false;
int value = -1;
for (int i = 0; i < ColorList.Length; i++)
if (ColorList[i].Remark.ToLower() == tagString.ToLower())
{
value = i;
break;
}
if (value != -1)
{
this._values[idx] = (ushort)value;
this.Items[idx].Enable = true;
return true;
}
else return false;
}
public Boolean setValue(int idx, bool value)
{
return setValue(idx, (ushort)(value ? 1 : 0));
}
public Boolean setValue(int row, int col, ushort value)
{
var idx = row * this.ColumnCount + col;
return setValue(idx, value);
}
public Boolean setValue(int row, int col, int value)
{
var idx = row * this.ColumnCount + col;
return setValue(idx, (ushort)value);
}
public Boolean setValueToggle(int row, int col, ushort value1,ushort value2)
{
var idx = row * this.ColumnCount + col;
if(getValue(idx) == value1) return setValue(idx, value2);
else return setValue(idx, value1);
}
public Boolean setValue(int row, int col, bool value)
{
var idx = row * this.ColumnCount + col;
return setValue(idx, (ushort)(value ? 1 : 0));
}
public Boolean setValue(int row, int col, string value)
{
var idx = row * this.ColumnCount + col;
return setValue(idx, value);
}
public ushort getValue(int idx)
{
if (idx >= _values.Length) return 0;
return _values[idx];
}
public ushort getValue(int row, int col)
{
var idx = row * this.ColumnCount + col;
return getValue(idx);
}
[Category("arFrame")]
public bool ShowIndexString { get { return _showIndexString; } set { _showIndexString = value; Invalidate(); } }
[Category("arFrame"), DisplayName("테두리 굵기")]
public int BorderSize { get { return bordersize; } set { this.bordersize = value; Invalidate(); } }
[Category("arFrame"), DisplayName("메뉴 테두리 굵기")]
public int MenuBorderSize { get { return menubordersize; } set { this.menubordersize = value; Invalidate(); } }
[Category("arFrame"), DisplayName("메뉴 간격")]
public int MenuGap { get { return menugap; } set { this.menugap = value; RemakeChartRect(); Invalidate(); } }
[Category("arFrame"), DisplayName("글자를 이미지 다음에 표시"), Description("이미지가 있는 경우 해당 이미지 옆에 글자를 붙입니다")]
public Boolean TextAttachToImage { get { return textattachtoimage; } set { this.textattachtoimage = value; Invalidate(); } }
[Category("arFrame"), DisplayName("색상-테두리")]
public System.Drawing.Color BorderColor { get { return bordercolor; } set { this.bordercolor = value; Invalidate(); } }
[Category("arFrame"), DisplayName("내부 여백")]
public new System.Windows.Forms.Padding Padding { get { return padding; } set { this.padding = value; RemakeChartRect(); Invalidate(); } }
[Category("arFrame"), DisplayName("색상-전체배경색")]
public override System.Drawing.Color BackColor { get { return backcolor; } set { this.backcolor = value; Invalidate(); } }
[Category("arFrame"), DisplayName("색상-글자(그림자)")]
public System.Drawing.Color ShadowColor { get { return _shadowColor; } set { _shadowColor = value; this.Invalidate(); } }
[Category("arFrame"), DisplayName("색상-글자")]
public override Color ForeColor { get { return base.ForeColor; } set { base.ForeColor = value; } }
[Category("arFrame"), DisplayName("색상-글자(번호)")]
public Color ForeColorPin { get { return foreColorPin; } set { foreColorPin = value; } }
[Category("arFrame"), DisplayName("글꼴-번호")]
public Font FontPin { get { return fontPin; } set { fontPin = value; Invalidate(); } }
[Category("arFrame"), DisplayName("글꼴-항목")]
public override Font Font { get { return base.Font; } set { base.Font = value; Invalidate(); } }
private int mouseOverItemIndex = -1;
public GridView()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
//값과 이름은 외부의 값을 사용한다
ResetArray();
if (MinimumSize.Width == 0 || MinimumSize.Height == 0)
MinimumSize = new Size(100, 50);
}
void ResetArray()
{
if (this._values != null) Array.Resize(ref this._values, itemCount);// = new UInt16[itemCount];
// if (this._titles != null) Array.Resize(ref this._titles, itemCount);//
// if (this._names != null) Array.Resize(ref this._names, itemCount);//
}
int itemCount { get { return ColumnCount * RowCount; } }
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
RemakeChartRect();
}
public event EventHandler<ItemClickEventArgs> ItemClick;
public class ItemClickEventArgs : EventArgs
{
public int idx { get; set; }
public GridViewItem Item { get; set; }
public ItemClickEventArgs(int idx_, GridViewItem item)
{
this.Item = item;
this.idx = idx_;
}
}
protected override void OnMouseClick(MouseEventArgs e)
{
//마우스클릭시 해당 버튼을 찾아서 반환한다.
if (Items == null || Items.Length < 1) return;
for (int i = 0; i < Items.Length; i++)
{
var rect = Items[i].rect;//[i];
if (rect.Contains(e.Location))
{
var menu = Items[i];
//미사용개체는 이벤트를 아에 발생하지 않는다
if (menu.Enable == true && ItemClick != null)
ItemClick(this, new ItemClickEventArgs(i, menu));
break;
}
}
}
protected override void OnMouseLeave(EventArgs e)
{
this.mouseOverItemIndex = -1;
this.Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (Items == null || Items.Length < 1)
{
this.mouseOverItemIndex = -1;
return;
}
for (int i = 0; i < Items.Length; i++)
{
var rect = Items[i].rect;// rects[i];
if (rect.Contains(e.Location))
{
if (i != mouseOverItemIndex)
{
mouseOverItemIndex = i;
this.Invalidate();
}
break;
}
}
}
public void setItemTextAlign(int row, int col, System.Drawing.ContentAlignment TextAlign)
{
var item = this.Items.Where(t => t.row == row && t.col == col).FirstOrDefault();
if (item != null) item.TextAlign = TextAlign;
}
public void setItemTextAlign(System.Drawing.ContentAlignment TextAlign)
{
foreach (var item in this.Items)
item.TextAlign = TextAlign;
}
protected override void OnPaint(PaintEventArgs pe)
{
//배경그리기
//using (var sb = new System.Drawing.Drawing2D.LinearGradientBrush(DisplayRectangle, BackColor, BackColor2On, System.Drawing.Drawing2D.LinearGradientMode.Vertical))
pe.Graphics.FillRectangle(new SolidBrush(BackColor), DisplayRectangle);
if (Items == null)
{
pe.Graphics.DrawString("no items", this.Font, Brushes.Red, 100, 100);
return;
}
var items = Items.OrderBy(t => t.No);
foreach (var menu in items)
{
drawItem(menu.idx, pe.Graphics);
}
//테두리 그리기
if (BorderSize > 0)
{
pe.Graphics.DrawRectangle(new Pen(this.BorderColor, BorderSize),
this.DisplayRectangle.Left,
this.DisplayRectangle.Top,
this.DisplayRectangle.Width - 1,
this.DisplayRectangle.Height - 1);
}
}
public void drawItem(int itemIndex, Graphics g = null)
{
if (g == null) g = this.CreateGraphics();
var menu = this.Items[itemIndex];
if (menu.rect == RectangleF.Empty) return;
var rect = menu.rect;// rects[i];
var diplayText = string.Empty;
//타이틀이 줄번호별로 처리됨
if (_titles != null && menu.row < _titles.Length && _titles[menu.row] != null )
{
var linebif = _titles[menu.row].Split('|');
if (menu.col < linebif.Length)
diplayText = linebif[menu.col];
}
UInt16 Value = 0;
if (_values != null && menu.idx < _values.Length) Value = _values[menu.idx];
//배경이 투명이 아니라면 그린다.
var bgColor1 = Color.FromArgb(30, 30, 30);// BackColor1Off;
var bgColor2 = Color.FromArgb(30, 30, 30);// BackColor2Off;
//해당 값에 따른 컬러값을 읽는다.
if (ColorList != null && Value < ColorList.Length)
{
bgColor1 = this.ColorList[Value].BackColor1;
bgColor2 = this.ColorList[Value].BackColor2;
}
using (var sb = new System.Drawing.Drawing2D.LinearGradientBrush(rect, bgColor1, bgColor2, System.Drawing.Drawing2D.LinearGradientMode.Vertical))
g.FillRectangle(sb, rect);
// if (mouseOverItemIndex == menu.idx)
// this.Cursor = Cursors.Hand;
// else
// this.Cursor = Cursors.Arrow;
//테두리를 그리는 속성과 트기가 설정된 경우에만 표시
//if (mouseOverItemIndex == i)
// {
// pe.Graphics.DrawRectangle(new Pen(Color.DeepSkyBlue), rect.Left, rect.Top, rect.Width, rect.Height);
//}
//else
{
if (MenuBorderSize > 0)
{
using (var p = new Pen(BorderColor, MenuBorderSize))
g.DrawRectangle(p, rect.Left, rect.Top, rect.Width, rect.Height);
}
}
//인덱스번호 출력
if (ShowIndexString && _names != null && menu.idx < _names.Length)
{
//표시글자
var idxstr = string.Format("[{0}] {1}", menu.idx, _names[menu.idx]);
//그림자 추가
if (ShadowColor != System.Drawing.Color.Transparent)
g.DrawString(idxstr, FontPin, new SolidBrush(ShadowColor), menu.rect.Left + 4, menu.rect.Top + 4);
//일반글자표시
g.DrawString(idxstr, FontPin, new SolidBrush(this.ForeColorPin), menu.rect.Left + 3, menu.rect.Top + 3);
}
if (diplayText != "")
{
using (StringFormat sf = new StringFormat(StringFormatFlags.NoClip))
{
//글자를 텍스트 이후에 붙이는 거라면?
if (menu.TextAlign == ContentAlignment.BottomCenter || menu.TextAlign == ContentAlignment.BottomLeft ||
menu.TextAlign == ContentAlignment.BottomRight) sf.LineAlignment = StringAlignment.Far;
else if (menu.TextAlign == ContentAlignment.MiddleCenter || menu.TextAlign == ContentAlignment.MiddleLeft ||
menu.TextAlign == ContentAlignment.MiddleRight) sf.LineAlignment = StringAlignment.Center;
else if (menu.TextAlign == ContentAlignment.TopCenter || menu.TextAlign == ContentAlignment.TopLeft ||
menu.TextAlign == ContentAlignment.TopRight) sf.LineAlignment = StringAlignment.Near;
if (menu.TextAlign == ContentAlignment.BottomCenter || menu.TextAlign == ContentAlignment.MiddleCenter ||
menu.TextAlign == ContentAlignment.TopCenter) sf.Alignment = StringAlignment.Center;
else if (menu.TextAlign == ContentAlignment.BottomLeft || menu.TextAlign == ContentAlignment.MiddleLeft ||
menu.TextAlign == ContentAlignment.TopLeft) sf.Alignment = StringAlignment.Near;
else if (menu.TextAlign == ContentAlignment.BottomRight || menu.TextAlign == ContentAlignment.MiddleRight ||
menu.TextAlign == ContentAlignment.TopRight) sf.Alignment = StringAlignment.Far;
//그림자 추가
if (ShadowColor != System.Drawing.Color.Transparent)
g.DrawString(diplayText, this.Font, new SolidBrush(ShadowColor),
new RectangleF((float)(rect.Left + 1f), (float)(rect.Top + 1f), (float)rect.Width, (float)rect.Height), sf);
g.DrawString(diplayText, this.Font, new SolidBrush(ForeColor), rect, sf);
}
}
if (showDebugInfo)
{
g.DrawString(Value.ToString(), this.fontPin, Brushes.SkyBlue, rect.Left, rect.Top);
}
}
/// <summary>
/// arFrame 전용 속성값을 복사 합니다
/// </summary>
/// <param name="ctl"></param>
public void copyTo(GridView ctl)
{
ctl.backcolor = this.backcolor;
ctl.menugap = this.menugap;
ctl.Items = this.Items;
ctl.menubordersize = this.menubordersize;
ctl.padding = this.padding;
ctl.ForeColor = this.ForeColor;
ctl.Font = this.Font;
ctl.TextAttachToImage = this.TextAttachToImage;
ctl.bordercolor = this.bordercolor;
ctl.bordersize = this.bordersize;
}
public void RemakeChartRect()
{
if (DisplayRectangle == Rectangle.Empty) return;
double x = 0;
double y = 0;
double w = DisplayRectangle.Width / (ColumnCount * 1.0);
double h = DisplayRectangle.Height / (RowCount * 1.0);
if (this.Items == null || itemCount != this.Items.Length)
{
//아이템갯수가 달라졌으므로 다시 갱신해야함
GridViewItem[] item = new GridViewItem[RowCount * ColumnCount];
for (int r = 0; r < RowCount; r++)
{
for (int c = 0; c < ColumnCount; c++)
{
int idx = r * ColumnCount + c;
item[idx] = new GridViewItem(idx, r, c);
item[idx].Enable = false;
item[idx].Padding = new Padding(0, 0, 0, 0);
item[idx].TextAlign = ContentAlignment.MiddleCenter;
x = (c * w);
y = (r * h);
item[idx].rect = new RectangleF((float)x, (float)y, (float)w, (float)h);
}
}
this.Items = item;
}
else
{
//아이템의 갯수는 같으므로 좌표값만 변경해준다.
for (int r = 0; r < RowCount; r++)
{
for (int c = 0; c < ColumnCount; c++)
{
int idx = r * ColumnCount + c;
var item = Items[idx];
x = (c * w);
y = (r * h);
item.Dirty = true;
item.rect = new RectangleF((float)x, (float)y, (float)w, (float)h);
}
}
}
this.Invalidate();
//int i = 0;
//var menuList = this.Items.OrderBy(t => t.No).ToArray();
//foreach (var menu in menuList)
//{
// int x, y, w, h;
// // var menu = menus[i];
// var mWidth = menuwidth;
// if (menu.MenuWidth > 0) mWidth = menu.MenuWidth;
// w = mWidth;
// h = DisplayRectangle.Height - Padding.Top - Padding.Bottom;
// if (menu.isRightMenu)
// {
// x = DisplayRectangle.Right - Padding.Right - (rightAcc) - (MenuGap * rightIdx);
// y = DisplayRectangle.Top + Padding.Top;
// rightAcc += 0;// = 0;// x;
// rightIdx += 1;
// }
// else
// {
// x = DisplayRectangle.Left + Padding.Left + leftAcc + (MenuGap * leftIdx);
// y = DisplayRectangle.Top + Padding.Top;
// leftAcc += mWidth;
// leftIdx += 1;
// }
// rects[i] = new Rectangle(x, y, w, h);
// i += 1;
//}
}
}
}

View File

@@ -0,0 +1,37 @@
namespace Narumi.UC
{
partial class GuideSensor
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Narumi.UC
{
public partial class GuideSensor : Control
{
public int SensorValue { get; set; } = 0;
public bool LMark { get; set; } = false;
public bool RMark { get; set; } = false;
public GuideSensor()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.Resize += arLabel_Resize;
}
void arLabel_Resize(object sender, EventArgs e)
{
Invalidate();
}
protected override void OnPaint(PaintEventArgs pe)
{
var boxcount = 11;
var r = new RectangleF(this.DisplayRectangle.Left + Padding.Left,
this.DisplayRectangle.Top + Padding.Top,
this.DisplayRectangle.Width - Padding.Left - Padding.Right,
this.DisplayRectangle.Height - Padding.Top - Padding.Bottom);
var term = 3;
var w = ((r.Width -1) - (term * (boxcount - 1))) / boxcount;
var h = r.Height -1;
pe.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.DisplayRectangle);
//pe.Graphics.DrawRectangle(Pens.Red, r.Left, r.Top, r.Width, r.Height);
for (int i = 0; i < boxcount; i++)
{
var x = r.Left + i * term + i * w;
var y = r.Top;
var r2 = new RectangleF(x, y, w, h);
if (this.Enabled == false)
{
pe.Graphics.FillRectangle(Brushes.LightGray, r2);
}
else
{
if (i == 0)
{
if (LMark)
pe.Graphics.FillRectangle(Brushes.SkyBlue, r2);
else
pe.Graphics.FillRectangle(Brushes.LightGray, r2);
}
else if (i == 9)
{
if (RMark)
pe.Graphics.FillRectangle(Brushes.SkyBlue, r2);
else
pe.Graphics.FillRectangle(Brushes.LightGray, r2);
}
else
{
if (SensorValue == i)
pe.Graphics.FillRectangle(Brushes.Tomato, r2);
else
pe.Graphics.FillRectangle(Brushes.LightGray, r2);
}
pe.Graphics.DrawRectangle(Pens.DimGray, r2.Left,r2.Top,r2.Width,r2.Height);
if (i == 0 || i == 10)
{
pe.Graphics.DrawString("M/K", this.Font,
new SolidBrush(this.ForeColor), r2,
new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
});
}
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
namespace AGVControl
{
partial class MapControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ClientSize = new System.Drawing.Size(800, 450);
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,621 @@
using AGVControl.Models;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;
namespace AGVControl
{
public class MapControlManager
{
public AGVActionPrediction PredictResult = null;
public AGV agv = new AGV();
public List<RFIDPoint> RFIDPoints = new List<RFIDPoint>();
public HashSet<RFIDConnection> rfidConnections = new HashSet<RFIDConnection>();
private ManualResetEvent mrepredict = new ManualResetEvent(true);
public MapControlManager()
{
}
~MapControlManager()
{
}
/// <summary>
/// 목표지정으로 모터방향이 이동하고 있는가?
/// history 데이터가 있어야 하며 기준데이터가 없는 경우 null 반환
/// </summary>
/// <returns></returns>
public bool? IsMotDirection_To_Target()
{
if (agv.MovementHistory.Any() == false || agv.MovementHistory.Count < 2) return null;
if (agv.MainPath.Any() == false) return null;
var prept = agv.MovementHistory.Skip(agv.MovementHistory.Count - 2).First();
var lstpt = agv.MovementHistory.Last();
//현재 이후의 경로를 가져온다
var curidx = agv.MainPath.FindIndex(t => t.Value == lstpt.Value);
var preidx = agv.MainPath.FindIndex(t => t.Value == prept.Value);
if (curidx == -1 || preidx == -1) return null;
//지정된경로 반대방향으로 이동하고 있다
return preidx < curidx;
}
public float GetDistance(Point p1, Point p2)
{
float dx = p1.X - p2.X;
float dy = p1.Y - p2.Y;
return (float)Math.Sqrt(dx * dx + dy * dy); // double을 float로 명시적 캐스팅
}
public AGVActionPrediction PredictNextAction()
{
if (mrepredict.WaitOne(1) == false)
{
PredictResult = CreatePrediction("이전 작업이 완료되지 않았습니다",
AGVActionReasonCode.busy,
AGVMoveState.Stop,
agv.Current_Motor_Direction, false);
return PredictResult;
}
mrepredict.Reset();
try
{
// 0. 설정경로와 리프트 방향 체크 (경로설정이 없을때에는 직선이동경로내의 방향들과 체크한다)
agv.IsTargetDirectionMatch = IsLiftDirectionMatch() ?? false;
// 1. 위치를 모를 때 (CurrentRFID가 0 또는 미설정)
if (agv.CurrentRFID.Value == 0)
{
PredictResult = CreatePrediction("AGV 위치 미확정(처음 기동)",
AGVActionReasonCode.NoPosition,
AGVMoveState.Run,
AgvDir.Backward, true,
moveSpeed: AgvSpeed.Low,
moveDiv: AgvSts.Straight);
return PredictResult;
}
//2. 이동방향을 모른다
if (agv.MovementHistory.Any() == false || agv.MovementHistory.Count < 2)
{
PredictResult = CreatePrediction("AGV이동방향 알수없음",
AGVActionReasonCode.NoDirection,
AGVMoveState.Run,
AgvDir.Backward, true,
moveSpeed: AgvSpeed.Low,
moveDiv: AgvSts.Straight);
return PredictResult;
}
// 3. 경로가 없거나 현재 위치가 경로에 없음
if ((agv.MainPath?.Count ?? 0) < 2 || agv.MainPath.Last().Value != agv.TargetRFID.Value)
{
//목적지가 없다면 진행할 수 없다
if (agv.TargetRFID == null)
{
PredictResult = CreatePrediction("경로 없음 또는 현재 위치 미확정",
AGVActionReasonCode.NoPath,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true);
return PredictResult;
}
else
{
//목적지가 있는데 경로가 없다면 경로 예측을 진행해야한다.
var CurPt = agv.MovementHistory.Last();
var prlt = CalculatePath(CurPt, agv.TargetRFID);
if (prlt.Success == false)
{
PredictResult = CreatePrediction("목적지 경로 예측 실패",
AGVActionReasonCode.Unknown,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true,
nextRFID: agv.TargetRFID);
return PredictResult;
}
else
{
//신규목적지에 대한 경로예측이 성공했다
agv.SubPath.Clear();
agv.MainPath = prlt.Path;
}
}
}
// 4. 경로상에서 다음 RFID 예측
int idx = agv.MainPath.FindIndex(p => p.Value == agv.CurrentRFID.Value);
if (idx < 0)
{
PredictResult = CreatePrediction("현재 위치가 경로에 없음",
AGVActionReasonCode.NotOnPath,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true);
return PredictResult;
}
// 4. 목적지 도달 전, 회전이 필요한경우인가?
// 목적지 RFID 정보
var destRFID = agv.MainPath.Last();
//리프트 방향이 맞는가?
var IsLiftDir = IsLiftDirectionMatch() ?? false;
//모션이동방향이 맞는가?
var IsMotDir = IsMotDirection_To_Target() ?? false;
var PrePT = agv.MovementHistory.Skip(agv.MovementHistory.Count - 2).First();
var curPT = agv.MovementHistory.Last();
//리프트방향이 맞지 않다면 회전가능한 위치로 이동을 해야한다
if (IsLiftDir == false)
{
AgvSpeed? agv_spd = null;
AgvSts? agv_dir = null;
//회전가능한 위치로 이동을 해야한다
//1. 가까운 회전위치를 찾는다
var nearTurnPoint = RFIDPoints.Where(t => t.IsRotatable)?.OrderBy(t => GetDistance(t.Location, agv.CurrentRFID.Location)).FirstOrDefault() ?? null;
if (nearTurnPoint == null)
{
PredictResult = CreatePrediction("회전 가능한 위치가 없습니다",
AGVActionReasonCode.NoTurnPoint,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true);
return PredictResult;
}
//2. 이동하기위한 경로계산 및 이동을 한다 (생성조건)
//2-1. 서브경로가없는경우
//2-2. 시작과 종료번호가 다른 경우(경로가 변경이 되는 조건이다)
if (agv.CurrentRFID.Value != nearTurnPoint.Value)
{
if (agv.SubPath.Any() == false || agv.SubPath.Count < 2 ||
agv.SubPath.First().Value != agv.CurrentRFID.Value ||
agv.SubPath.Last().Value != nearTurnPoint.Value)
{
var rlt = CalculatePath(agv.CurrentRFID, nearTurnPoint); //이전포인트도 추가를 해준다
if (rlt.Success) agv.SubPath = rlt.Path;
else
{
agv.SubPath.Clear();
PredictResult = CreatePrediction("회전 위치까지의 경로를 계산할 수 없습니다",
AGVActionReasonCode.PathCalcError,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true,
nextRFID: nearTurnPoint);
return PredictResult;
}
}
//현재 모터방향을 확인하여 대상까지 이동하도록 해야한다
var curidx = agv.SubPath.FindIndex(t => t.Value == curPT.Value);
var preidx = agv.SubPath.FindIndex(t => t.Value == PrePT.Value);
AgvDir newdirection = agv.Current_Motor_Direction;
string message = "턴위치로 이동중";
if (preidx > curidx)
{
//지정경로를 거꾸로 이동하고 있다
if (agv.Current_Motor_Direction == AgvDir.Forward)
newdirection = AgvDir.Backward;
else
newdirection = AgvDir.Forward;
message += "(방향전환)";
}
//도로정보를 확인하여 속도와 분기명령을 실행한다
var roadinfo = GetRoadInfo(agv.SubPath, curPT);
agv_spd = roadinfo.spd;
agv_dir = roadinfo.dir;
PredictResult = CreatePrediction(message,
AGVActionReasonCode.MoveForTurn,
AGVMoveState.Run,
newdirection, true,
moveSpeed: agv_spd,
moveDiv: agv_dir,
nextRFID: nearTurnPoint);
return PredictResult;
}
var roadinfo2 = GetRoadInfo(agv.SubPath.Any() ? agv.SubPath : agv.MainPath, curPT);
agv_spd = roadinfo2.spd;
agv_dir = roadinfo2.dir;
PredictResult = CreatePrediction("턴(이동) 완료 대기",
AGVActionReasonCode.NeedTurnMove,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true,
moveSpeed: agv_spd,
moveDiv: agv_dir,
nextRFID: nearTurnPoint);
return PredictResult;
}
//보조이동선제거
if (agv.SubPath != null && agv.SubPath.Any())
agv.SubPath.Clear();
//현재위치의 RFID에서 턴이 필요한경우
if (agv.CurrentRFID.NeedTurn)
{
//Turn이 완료되지 않았다면 턴 완료를 대기한다.
if (agv.CurrentRFID.TurnOK == false)
{
//아직 턴위치에 멈추지 않았다
if (agv.CurrentRFID.TurnStop == false)
{
//현재위치로 마크스탑이동을 하게한다
PredictResult = CreatePrediction("Wait for Turn(P)-Mark Stop",
AGVActionReasonCode.WaitForMarkStop,
AGVMoveState.Run,
agv.Current_Motor_Direction, true,
moveSpeed: agv.CurrentSpeed,
moveDiv: agv.CurrentSTS);
}
else
{
//턴위치에 정지했으니. 턴완료를 기다려야 한다
PredictResult = CreatePrediction("Wait for Turn(P)",
AGVActionReasonCode.NeedTurnPoint,
AGVMoveState.Run,
agv.Current_Motor_Direction, true,
moveSpeed: agv.CurrentSpeed,
moveDiv: agv.CurrentSTS);
}
return PredictResult;
}
}
//3. 목적지위치까지 이동이 완료되지 않았다면 계속 이동을 하게한다
if (agv.CurrentRFID.Value != destRFID.Value)
{
//현재 모터방향을 확인하여 대상까지 이동하도록 해야한다
var curidx = agv.MainPath.FindIndex(t => t.Value == curPT.Value);
var preidx = agv.MainPath.FindIndex(t => t.Value == PrePT.Value);
AgvDir newdirection = agv.Current_Motor_Direction;
string message = "목적지 이동중";
if (preidx > curidx)
{
//지정경로를 거꾸로 이동하고 있다
if (agv.Current_Motor_Direction == AgvDir.Forward)
newdirection = AgvDir.Backward;
else
newdirection = AgvDir.Forward;
message += "(방향전환)";
}
//경로상 바로 다음 위치를 확인한다
var nexstRFID = agv.MainPath.Skip(agv.MainPath.FindIndex(t => t.Value == curPT.Value) + 1).First();
var roadinfo = GetRoadInfo(agv.MainPath, curPT);
PredictResult = CreatePrediction(message,
AGVActionReasonCode.Normal,
AGVMoveState.Run,
newdirection, true,
moveSpeed: roadinfo.spd,
moveDiv: roadinfo.dir,
nextRFID: nexstRFID);
return PredictResult;
}
// 5. 목적지 도달 시
PredictResult = CreatePrediction("경로의 마지막 지점(목적지 도달)",
AGVActionReasonCode.Arrived,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true,
nextRFID: destRFID);
return PredictResult;
}
catch (Exception ex)
{
PredictResult = CreatePrediction($"ERR:{ex.Message}",
AGVActionReasonCode.Unknown,
AGVMoveState.Stop,
agv.Current_Motor_Direction, true);
return PredictResult;
}
finally
{
mrepredict.Set();
}
}
(AgvSpeed? spd, AgvSts? dir, RFIDConnection info) GetRoadInfo(List<RFIDPoint> paths, RFIDPoint curPT)
{
//도로정보를 확인하여 속도와 분기명령을 실행한다
AgvSpeed? agv_spd = null;
AgvSts? agv_div = null;
RFIDConnection info = null;
var nextpt = paths.Skip(paths.FindIndex(t => t.Value == curPT.Value) + 1).FirstOrDefault();
if (nextpt != null)
{
var p1 = rfidConnections.Where(t => t.P1.Value == curPT.Value && t.P2.Value == nextpt.Value).FirstOrDefault();
if (p1 != null)
{
//positive
agv_spd = p1.MoveSpeedP;
agv_div = p1.MoveDirectionP;
info = p1;
}
var p2 = rfidConnections.Where(t => t.P2.Value == curPT.Value && t.P1.Value == nextpt.Value).FirstOrDefault();
if (p2 != null)
{
//negative
agv_spd = p2.MoveSpeedN;
agv_div = p2.MoveDirectionN;
info = p2;
}
}
return (agv_spd, agv_div, info);
}
/// <summary>
/// 이웃포인터를 반환합니다
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public List<RFIDPoint> GetNeighbors(RFIDPoint pt)
{
var neighbors = new List<RFIDPoint>();
//값이 없는 경우 오류 반환
if (pt == null) return neighbors;
//연결정보에서 데이터를 찾은 후 반환한다
foreach (var connection in rfidConnections)
{
RFIDPoint nPT = null;
if (connection.P1.Value == pt.Value)
{
nPT = connection.P2;
}
else if (connection.P2.Value == pt.Value)
{
nPT = connection.P1;
}
if (nPT != null) neighbors.Add(nPT);
}
//중복제거후 반한
return neighbors.Distinct().ToList();
}
public RFIDPoint FindRFIDPoint(uint rfidValue)
{
if (RFIDPoints == null || RFIDPoints.Any() == false) return null;
return RFIDPoints.FirstOrDefault(r => r.Value == rfidValue);
}
private float Heuristic(Point a, Point b)
{
return (float)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2));
}
private PathResult ReconstructPath(Dictionary<uint, RFIDPoint> cameFrom, RFIDPoint current)
{
var path = new List<RFIDPoint> { current };
while (cameFrom.ContainsKey(current.Value))
{
current = cameFrom[current.Value];
if (path.Contains(current) == false)
path.Insert(0, current);
else
break; //왜인지 반복되는경우가있어 종료한다
}
return new PathResult
{
Path = path,
};
}
public PathResult CalculatePath(RFIDPoint start, RFIDPoint end)
{
var openList = new List<RFIDPoint> { start };
var closedList = new List<RFIDPoint>();
var cameFrom = new Dictionary<uint, RFIDPoint>();
var gScore = new Dictionary<RFIDPoint, float> { { start, 0 } };
var fScore = new Dictionary<RFIDPoint, float> { { start, Heuristic(start.Location, end.Location) } };
if (start.Value == end.Value)
{
return new PathResult
{
Message = "시작위치와 대상위치가 동일 합니다",
Path = null,
};
}
//if (autorun) openList.Clear();
while (openList.Count > 0)
{
var current = openList.OrderBy(p => fScore.ContainsKey(p) ? fScore[p] : float.MaxValue).First();
if (current.Value == end.Value)
{
return ReconstructPath(cameFrom, current);
}
openList.Remove(current);
closedList.Add(current);
var nb = GetNeighbors(current);
foreach (var neighbor in nb)
{
if (closedList.Contains(neighbor))
continue;
float tentativeGScore = gScore[current] + GetDistance(current.Location, neighbor.Location);
if (!openList.Contains(neighbor))
openList.Add(neighbor);
else if (tentativeGScore >= gScore[neighbor])
continue;
cameFrom[neighbor.Value] = (RFIDPoint)current;
gScore[neighbor] = tentativeGScore;
fScore[neighbor] = gScore[neighbor] + Heuristic(neighbor.Location, end.Location);
}
}
return new PathResult
{
Path = openList,
};
}
public PathResult CalculatePath(uint tagStrt, uint tagEnd)
{
var retval = new PathResult
{
Message = string.Empty,
Path = new List<RFIDPoint>(),
};
var startPoint = FindRFIDPoint(tagStrt);
var endPoint = FindRFIDPoint(tagEnd);
if (startPoint == null || endPoint == null)
{
retval.Message = "유효한 RFID 값을 입력해주세요.";
return retval;
}
retval = CalculatePath(startPoint, endPoint);
if (retval.Success == false)
retval.Message = "경로를 찾을 수 없습니다";
return retval;
}
/// <summary>
/// 리프트방향과 대상위치와의 방향이 일치하는가?
/// 목적지경로가 셋팅된 경우 현재 이동방향이 목적지방향과 일치하는가?
/// 이동경로정보가 없거나 목적지가 없으면 null 이 반환됨
/// </summary>
/// <returns></returns>
public bool? IsLiftDirectionMatch()
{
if (agv.MovementHistory.Any() && agv.MovementHistory.Count > 1)
{
RFIDPoint TargetPT = null;
var prept = agv.MovementHistory.Skip(agv.MovementHistory.Count - 2).First();
var lstpt = agv.MovementHistory.Last();
//뒤로이동하는경우라면 이전위치에 리프트가 있다.
if (lstpt.Direction == AgvDir.Backward)
{
TargetPT = prept;
}
else //앞으로 이동한다면 이동방향과 동일하다
{
//이전위치는 제거 하고 처음발견된 것을 대상으로 한다
TargetPT = GetNeighbors(lstpt).Where(t => t.Value != prept.Value).FirstOrDefault();
}
//목적지가 있다면 목적지의 방향과 일치하는지 확인해야한다
//남은경로중에 방향이 고정된 핀이 있다면 그것과 일치하는지 확인해야 한다
if (agv.MainPath.Any())
{
//지정된경로 반대방향으로 이동하고 있다
if ((IsMotDirection_To_Target() ?? false) == false)
{
return false;
}
else
{
var nextRoutes = agv.MainPath.Skip(agv.MainPath.FindIndex(t => t.Value == lstpt.Value) + 1).ToList();
var DirectionMatch = true;
foreach (var item in nextRoutes)
{
if (item.FixedDirection != null && item.FixedDirection != lstpt.Direction)
{
DirectionMatch = false;
break;
}
}
return DirectionMatch;
}
}
else
{
//대상포인트와의 방향만 체크한다.
//고정대상이없다면 방향이 맞는것으로 한다
return (TargetPT.FixedDirection ?? lstpt.Direction) == lstpt.Direction;
}
}
else
{
//이동된경로정보가 없다면 리프트 방향을 체크할 수 없으므로 대상과 위치가 맞지 않는걸로 기본값을 설정한다
//이렇게 설정하면 대상으로 이동불가하고 뒤로 가도록 유도된다
return null;
}
}
// Changed 속성 설정을 위한 헬퍼 메서드
private bool IsPredictionChanged(AGVActionPrediction newPrediction)
{
if (PredictResult == null) return true; // 이전 예측이 없으면 변경됨
// Idx와 Changed를 제외하고 비교
return !PredictResult.Equals(newPrediction);
}
private AGVActionPrediction CreatePrediction(
string reason,
AGVActionReasonCode reasonCode,
AGVMoveState moveState,
AgvDir direction, bool IDXUpdate = true,
AgvSpeed? moveSpeed = null,
AgvSts? moveDiv = null,
RFIDPoint nextRFID = null
)
{
var newPrediction = new AGVActionPrediction
{
NextRFID = nextRFID,
Reason = reason,
ReasonCode = reasonCode,
MoveState = moveState,
Direction = direction,
MoveSpeed = moveSpeed,
MoveDiv = moveDiv,
Idx = IDXUpdate ? (PredictResult?.Idx + 1 ?? 1) : (PredictResult?.Idx ?? 0),
CreateTime = DateTime.Now,
};
newPrediction.Changed = IsPredictionChanged(newPrediction);
return newPrediction;
}
}
}

View File

@@ -0,0 +1,352 @@
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Windows.Forms;
namespace AGVControl.Models
{
//public class CRFIDData
//{
// public UInt16 Value { get; set; }
// public Point Location { get; set; }
// public override string ToString()
// {
// return $"RFID:{Value},P:{Location.X},{Location.Y}";
// }
//}
public class movehistorydata : RFIDPoint
{
public AgvDir Direction { get; set; }
public override string ToString()
{
return $"RFID:{Value},DIR:{Direction},P:{Location.X},{Location.Y}";
}
}
public class AGV
{
/// <summary>
/// RFID 번호
/// </summary>
public RFIDPoint CurrentRFID { get; set; }
/// <summary>
/// 목적지가 셋팅된경우 해당 값
/// </summary>
public RFIDPoint TargetRFID { get; set; }
/// <summary>
/// 배터리잔량(%)
/// </summary>
public float BatteryLevel { get; set; } = 0f;
/// <summary>
/// 배터리온도(board)
/// </summary>
public double BatteryTemp1 { get; set; } = 0;
/// <summary>
/// 배터리온도(cell)
/// </summary>
public double BatteryTemp2 { get; set; } = 0;
/// <summary>
/// AGV Speed
/// </summary>
public AgvSpeed CurrentSpeed { get; set; }
/// <summary>
/// AGV STS
/// </summary>
public AgvSts CurrentSTS { get; set; }
/// <summary>
/// AGV Motor Direction
/// </summary>
public AgvDir Current_Motor_Direction { get; set; }
/// <summary>
/// 현재위치가 수산되면 목적지까지의 방향값이 계산됩니다.
/// </summary>
public AgvDir? TargetDirection { get; set; }
/// <summary>
/// AGV.System1.agv_Run
/// </summary>
public bool IsMoving { get; set; }
/// <summary>
/// AGV.System1.Mark1_Check | Mark2_Check
/// </summary>
public bool IsMarkCheck { get; set; }
/// <summary>
/// 이동대상과 AGV의 머리방향이 일치하는지?
/// </summary>
public bool IsTargetDirectionMatch { get; set; }
/// <summary>
/// 메인경로
/// 경로검색으로 입력된 경로
/// </summary>
public List<RFIDPoint> MainPath { get; set; } = new List<RFIDPoint>();
/// <summary>
/// 메인경로외에 거쳐가는 중간 경로
/// </summary>
public List<RFIDPoint> SubPath { get; set; }
public List<string> PathRFIDs { get; set; }
// 이동 경로 기록을 위한 새로운 속성들
public List<movehistorydata> MovementHistory { get; } = new List<movehistorydata>();
public const int HISTORY_SIZE = 10; // 최근 4개 위치 기록
public AGV()
{
MainPath = new List<RFIDPoint>();
SubPath = new List<RFIDPoint>();
PathRFIDs = new List<string>();
CurrentRFID = new RFIDPoint();
TargetRFID = new RFIDPoint();
TargetDirection = AgvDir.Forward;
// BodyAngle = null;
}
// 이동 경로에 새로운 RFID 추가
public void AddToMovementHistory(UInt16 rfidValue, Point position, AgvDir direction)
{
// 중복 RFID가 연속으로 들어오는 경우 무시
if (MovementHistory.Count > 0 && MovementHistory.Last().Value == rfidValue)
return;
MovementHistory.Add(new movehistorydata { Value = rfidValue, Direction = direction, Location = position });
// 기록 크기 제한
if (MovementHistory.Count > HISTORY_SIZE)
{
MovementHistory.RemoveAt(0);
}
//최초방향과 마지막 방향이 일치하지 않으면 그 이전의 데이터는 삭제한다.
if (MovementHistory.Count > 2 && MovementHistory.First().Direction != MovementHistory.Last().Direction)
{
var lastTwo = MovementHistory.Skip(MovementHistory.Count - 2).Take(2).ToArray(); // [9, 10]
MovementHistory.Clear();
MovementHistory.AddRange(lastTwo);
}
// 위치 업데이트 시 자동으로 히스토리 파일에 저장
SaveHistoryOnUpdate();
}
// 연결 정보 기반 실제 이동 방향 계산
public AgvDir? CalculateActualDirectionByConnection(uint currentRFID, uint previousRFID, List<RFIDConnection> connections)
{
if (connections == null || connections.Count == 0)
return null;
// 이전 RFID에서 현재 RFID로의 연결 확인
var connection = connections.FirstOrDefault(c =>
(c.P1.Value == previousRFID && c.P2.Value == currentRFID) ||
(c.P1.Value == currentRFID && c.P2.Value == previousRFID));
if (connection == null)
return null; // 연결되지 않은 경로
// 연결 방향에 따라 실제 이동 방향 결정
if (connection.P1.Value == previousRFID && connection.P2.Value == currentRFID)
{
return AgvDir.Forward; // Start -> End 방향으로 이동
}
else
{
return AgvDir.Backward; // End -> Start 방향으로 이동
}
}
// 연결 정보 기반 방향 불일치 검증 및 정정
public bool ValidateAndCorrectDirectionByConnection(AgvDir expectedDirection, List<RFIDConnection> connections)
{
if (MovementHistory.Count < 2 || connections == null)
return true; // 검증 불가능한 경우
// 최근 두 RFID 값 가져오기
var recentRFIDs = MovementHistory.Skip(MovementHistory.Count - 2).Take(2).ToList();
if (recentRFIDs.Count < 2)
return true;
var previousRFID = recentRFIDs[0];
var currentRFID = recentRFIDs[1];
var actualDirection = CalculateActualDirectionByConnection(currentRFID.Value, previousRFID.Value, connections);
if (!actualDirection.HasValue)
return true; // 연결 정보로 방향 판단 불가
// 방향이 일치하지 않는 경우
if (actualDirection.Value != expectedDirection)
{
// AGV 모터 방향을 실제 이동 방향으로 정정
//CurrentAGVDirection = actualDirection.Value;
TargetDirection = actualDirection.Value;
return false; // 정정됨을 알림
}
return true; // 방향 일치
}
// RFID 순서 기반 실제 이동 방향 계산 (기존 메서드 - 호환성 유지)
public AgvDir? CalculateActualDirectionByRFID()
{
if (MovementHistory.Count < 2)
return null;
// 최근 두 RFID 값으로부터 실제 이동 방향 계산
var recentRFIDs = MovementHistory.Skip(Math.Max(0, MovementHistory.Count - 2)).Take(2).ToList();
if (recentRFIDs.Count < 2)
return null;
var prevRFID = recentRFIDs[0];
var currentRFID = recentRFIDs[1];
// RFID 값의 증가/감소로 방향 판단
if (currentRFID.Value > prevRFID.Value)
{
return AgvDir.Forward; // RFID 값이 증가하면 전진
}
else if (currentRFID.Value < prevRFID.Value)
{
return AgvDir.Backward; // RFID 값이 감소하면 후진
}
else
{
return null; // 같은 RFID 값이면 방향 판단 불가
}
}
// 위치 히스토리 파일 저장 (최근 3개만 저장)
public void SavePositionHistory(string filePath)
{
try
{
// 최근 3개의 히스토리만 저장
var recentHistory = MovementHistory.Skip(Math.Max(0, MovementHistory.Count - 3)).ToList();
var lines = new List<string>();
lines.Add($"# AGV Position History - {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
lines.Add("# Format: RFID,Direction,X,Y,Timestamp");
foreach (var history in recentHistory)
{
lines.Add($"{history.Value},{history.Direction},{history.Location.X},{history.Location.Y},{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
}
System.IO.File.WriteAllLines(filePath, lines);
}
catch (Exception ex)
{
// 로그 기록 (실제 환경에서는 로깅 시스템 사용)
System.Diagnostics.Debug.WriteLine($"SavePositionHistory Error: {ex.Message}");
}
}
// 위치 히스토리 파일 로드
public bool LoadPositionHistory(string filePath)
{
try
{
if (!System.IO.File.Exists(filePath))
return false;
var lines = System.IO.File.ReadAllLines(filePath);
MovementHistory.Clear();
foreach (var line in lines)
{
// 주석 라인 건너뛰기
if (line.StartsWith("#") || string.IsNullOrWhiteSpace(line))
continue;
var parts = line.Split(',');
if (parts.Length >= 4)
{
if (UInt16.TryParse(parts[0], out UInt16 rfidValue) &&
Enum.TryParse<AgvDir>(parts[1], out AgvDir direction) &&
int.TryParse(parts[2], out int x) &&
int.TryParse(parts[3], out int y))
{
MovementHistory.Add(new movehistorydata
{
Value = rfidValue,
Direction = direction,
Location = new Point(x, y)
});
}
}
}
return MovementHistory.Count > 0;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"LoadPositionHistory Error: {ex.Message}");
return false;
}
}
// 시작 시 위치 히스토리 자동 로드
public void LoadHistoryOnStartup()
{
string historyFilePath = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
"agv_position_history.dat"
);
LoadPositionHistory(historyFilePath);
}
// 위치 업데이트 시 자동 저장
public void SaveHistoryOnUpdate()
{
string historyFilePath = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
"agv_position_history.dat"
);
SavePositionHistory(historyFilePath);
}
}
public class PathNode
{
public Point Location { get; set; }
public string RFID { get; set; }
public double G { get; set; } // 시작점에서 현재 노드까지의 비용
public double H { get; set; } // 현재 노드에서 목표점까지의 예상 비용
public double F => G + H; // 총 비용
public PathNode Parent { get; set; }
public PathNode(Point location, string rfid)
{
Location = location;
RFID = rfid;
G = 0;
H = 0;
Parent = null;
}
}
}

View File

@@ -0,0 +1,58 @@
using AGVControl.Models;
using System;
namespace AGVControl
{
public class AGVActionPrediction
{
public AgvDir Direction { get; set; }
public RFIDPoint NextRFID { get; set; }
public string Reason { get; set; }
public AGVActionReasonCode ReasonCode { get; set; }
public AGVMoveState MoveState { get; set; } // RUN 또는 STOP
public AgvSpeed? MoveSpeed { get; set; }
public AgvSts? MoveDiv { get; set; }
public UInt32 Idx { get; set; }
public bool Changed { get; set; }
public DateTime CreateTime { get; set; }
// override object.Equals
public bool Equals(AGVActionPrediction obj)
{
// null 체크
if (obj == null) return false;
// 참조가 같으면 true
if (ReferenceEquals(this, obj)) return true;
// 핵심 속성들만 비교 (Idx, Changed 제외)
if (obj.Direction != this.Direction) return false;
if (obj.ReasonCode != this.ReasonCode) return false;
if (obj.MoveState != this.MoveState) return false;
if (obj.MoveSpeed != this.MoveSpeed) return false;
if (obj.MoveDiv != this.MoveDiv) return false;
// NextRFID 비교 (null 체크 포함)
if (obj.NextRFID == null || this.NextRFID == null)
{
if (obj.NextRFID != this.NextRFID) return false; // 하나만 null이면 false
}
else
{
if (obj.NextRFID.Value != this.NextRFID.Value) return false;
}
// Reason 비교 (null 체크 포함)
if (obj.Reason == null || this.Reason == null)
{
if (obj.Reason != this.Reason) return false; // 하나만 null이면 false
}
else
{
if (obj.Reason != this.Reason) return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Drawing;
using System;
namespace AGVControl.Models
{
public class CustomLine
{
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public Color LineColor { get; set; }
public int LineWidth { get; set; }
}
}

View File

@@ -0,0 +1,86 @@
using System.Drawing;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace AGVControl.Models
{
/// <summary>
/// 도로특성정보
/// </summary>
public class RoadInformation
{
/// <summary>
/// 시작지점
/// </summary>
public RFIDPoint P1 { get; set; }
/// <summary>
/// 종료지점
/// </summary>
public RFIDPoint P2 { get; set; }
/// <summary>
/// 도로의 사용여부
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// AGV의 이동방향(리프트방향)
/// 목적지 방향과의 일치를 위해 해당 방향을 설정할 수 있따
/// </summary>
public AgvDir? LiftDirection { get; set; }
/// <summary>
/// AGV이동시 속도 (high, middle, low)
/// </summary>
public AgvSpeed? MoveSpeed { get; set; }
/// <summary>
/// AGV이동시 방향모드(좌분기, 전진, 우분기)
/// </summary>
public AgvSts? MoveDirection { get; set; }
public RoadInformation()
{
P1 = null;
P2 = null;
LiftDirection = null;
MoveSpeed = null;
MoveDirection = null;
Enable = false;
}
/// <summary>
/// 값이 설정되어있는지
/// </summary>
public bool HasValue
{
get
{
if (P1 == null || P2 == null) return false;
if (LiftDirection == null && MoveSpeed == null && MoveDirection == null) return false;
return true;
}
}
}
public class MagnetLine
{
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public List<Point> BranchPoints { get; set; }
public Dictionary<Point, BranchDirection> BranchDirections { get; set; }
public MagnetLine()
{
BranchPoints = new List<Point>();
BranchDirections = new Dictionary<Point, BranchDirection>();
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Drawing;
using System;
using System.Collections.Generic;
namespace AGVControl.Models
{
public enum BranchDirection
{
Left,
Straight,
Right
}
}

View File

@@ -0,0 +1,63 @@
using System.Drawing;
using System;
using System.Collections.Generic;
namespace AGVControl.Models
{
public class MapText
{
private bool _dirty = true;
private Point _location;
private string _text;
private Font _font;
public bool Dirty
{
get => _dirty;
set => _dirty = value;
}
public Point Location
{
get => _location;
set
{
if (_location != value)
{
_location = value;
_dirty = true;
}
}
}
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
_dirty = true;
}
}
}
public Color TextColor { get; set; }
public Color BackgroundColor { get; set; }
public Font Font
{
get => _font;
set
{
if (_font != value)
{
_font = value;
_dirty = true;
}
}
}
public RectangleF Bounds { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
using AGVControl.Models;
using System.Collections.Generic;
using System.Linq;
namespace AGVControl
{
public class PathResult
{
public bool Success
{
get
{
return Path != null && Path.Any();
}
}
public string Message { get; set; }
public List<RFIDPoint> Path { get; set; }
}
}

View File

@@ -0,0 +1,214 @@
using AGVControl.Models;
using AR;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.Tracing;
namespace AGVControl
{
public class RFIDConnection
{
/// <summary>
/// 시작지점
/// </summary>
public RFIDPoint P1 { get; set; }
/// <summary>
/// 종료지점
/// </summary>
public RFIDPoint P2 { get; set; }
/// <summary>
/// 도로의 사용여부
/// </summary>
public bool EnableP { get; set; }
public bool EnableN { get; set; }
/// <summary>
/// AGV의 이동방향(리프트방향)
/// 목적지 방향과의 일치를 위해 해당 방향을 설정할 수 있따
/// </summary>
public AgvDir? LiftDirectionP { get; set; }
public AgvDir? LiftDirectionN { get; set; }
/// <summary>
/// AGV이동시 속도 (high, middle, low)
/// </summary>
public AgvSpeed? MoveSpeedP { get; set; }
public AgvSpeed? MoveSpeedN { get; set; }
/// <summary>
/// AGV이동시 방향모드(좌분기, 전진, 우분기)
/// </summary>
public AgvSts? MoveDirectionP { get; set; }
public AgvSts? MoveDirectionN { get; set; }
/// <summary>
/// 파일저장 및 불러오기시 사용하는 문자열로 반환
/// </summary>
[Browsable(false)]
public string DataFileString
{
get
{
var str_enbP = EnableP ? "1" : "0";
var str_liftP = "";
if (LiftDirectionP != null) str_liftP = ((int)LiftDirectionP).ToString();
var str_movespeedP = "";
if (MoveSpeedP != null) str_movespeedP = ((int)MoveSpeedP).ToString();
var str_movdirP = "";
if (MoveDirectionP != null) str_movdirP = ((int)MoveDirectionP).ToString();
var str_enbN = EnableP ? "1" : "0";
var str_liftN = "";
if (LiftDirectionN != null) str_liftN = ((int)LiftDirectionN).ToString();
var str_movespeedN = "";
if (MoveSpeedN != null) str_movespeedN = ((int)MoveSpeedN).ToString();
var str_movdirN = "";
if (MoveDirectionN != null) str_movdirN = ((int)MoveDirectionN).ToString();
var PStr= $"{P1.Location.X},{P1.Location.Y},{P2.Location.X},{P2.Location.Y}," + //location
$"{P1.Value},{P2.Value}," + //rfid values
$"{str_enbP};{str_liftP};{str_movespeedP};{str_movdirP}," +
$"{str_enbN};{str_liftN};{str_movespeedN};{str_movdirN}";
return $"{PStr}";
}
set
{
var buf = value.Split(',');
if (buf.Length >= 2)
{
var p1x = int.Parse(buf[0]);
var p1y = int.Parse(buf[1]);
var p2x = int.Parse(buf[2]);
var p2y = int.Parse(buf[3]);
var p1v = ushort.Parse(buf[4]);
var p2v = ushort.Parse(buf[5]);
if (P1 == null) P1 = new RFIDPoint();
P1.Location = new System.Drawing.Point(p1x, p1y);
P1.Value = p1v;
if (P2 == null) P2 = new RFIDPoint();
P2.Location = new System.Drawing.Point(p2x, p2y);
P2.Value = p2v;
if (buf[6].Contains(";")) //양방향 정보
{
var StrP = buf[6].Split(';');
var StrN = buf[7].Split(';');
//Positive
this.EnableP = StrP[0] == "1";
if (StrP[1].isEmpty()) LiftDirectionP = null;
else LiftDirectionP = (AgvDir)int.Parse(StrP[1]);
if (StrP[2].isEmpty()) MoveSpeedP = null;
else MoveSpeedP = (AgvSpeed)int.Parse(StrP[2]);
if (StrP[3].isEmpty()) MoveDirectionP = null;
else MoveDirectionP = (AgvSts)int.Parse(StrP[3]);
//Negative
this.EnableN = StrN[0] == "1";
if (StrN[1].isEmpty()) LiftDirectionN = null;
else LiftDirectionN = (AgvDir)int.Parse(StrN[1]);
if (StrN[2].isEmpty()) MoveSpeedN = null;
else MoveSpeedN = (AgvSpeed)int.Parse(StrN[2]);
if (StrN[3].isEmpty()) MoveDirectionN = null;
else MoveDirectionN = (AgvSts)int.Parse(StrN[3]);
}
else
{
this.EnableP = buf[6] == "1";
if (buf[7].isEmpty()) LiftDirectionP = null;
else LiftDirectionP = (AgvDir)int.Parse(buf[7]);
if (buf[8].isEmpty()) MoveSpeedP = null;
else MoveSpeedP = (AgvSpeed)int.Parse(buf[8]);
if (buf[9].isEmpty()) MoveDirectionP = null;
else MoveDirectionP = (AgvSts)int.Parse(buf[9]);
this.EnableN = this.EnableP;
this.LiftDirectionN = this.LiftDirectionP;
this.MoveSpeedN = this.MoveSpeedP;
this.MoveDirectionN = this.MoveDirectionP;
}
}
}
}
public RFIDConnection(string dataline = "")
{
P1 = null;
P2 = null;
LiftDirectionP = null;
MoveSpeedP = null;
MoveDirectionP = null;
EnableP = false;
LiftDirectionN = null;
MoveSpeedN = null;
MoveDirectionP = null;
EnableP = false;
if (dataline.isEmpty() == false) DataFileString = dataline;
}
/// <summary>
/// 값이 설정되어있는지
/// </summary>
public bool HasValue
{
get
{
if (P1 == null || P2 == null) return false;
if (LiftDirectionP == null && MoveSpeedP == null && MoveDirectionP == null &&
LiftDirectionN == null && MoveSpeedN == null && MoveDirectionN == null) return false;
return true;
}
}
public override bool Equals(object obj)
{
if (obj is RFIDConnection other)
{
return (P1 == other.P1 && P2 == other.P2);
}
return false;
}
public override int GetHashCode()
{
return P1.GetHashCode() ^ P2.GetHashCode();
}
public override string ToString()
{
//연결정보를 확인
return $"{P1.Value} ↔ {P2.Value},ENB:{(EnableP ? "O" : "X")}|{(EnableN ? "O" : "X")}";
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Drawing;
using System;
using System.Collections.Generic;
namespace AGVControl.Models
{
public class RFIDLine
{
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public float Distance
{
get {
float dx = StartPoint.X - EndPoint.X;
float dy = StartPoint.Y - EndPoint.Y;
return (float)Math.Sqrt(dx * dx + dy * dy); // double을 float로 명시적 캐스팅
}
}
//public uint StartRFID { get; set; }
//public uint EndRFID { get; set; }
//public bool IsBidirectional { get; set; } = true; // 양방향 이동 가능 여부
//public float Distance { get; set; } // 두 RFID 포인트 사이의 거리
//public List<uint> ConnectedRFIDs { get; set; } = new List<uint>(); // 연결된 모든 RFID 값들
//public Dictionary<uint, uint> NextRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 다음 RFID
//public Dictionary<uint, uint> PrevRFID { get; set; } = new Dictionary<uint, uint>(); // 각 RFID의 이전 RFID
}
}

View File

@@ -0,0 +1,59 @@
using System.Drawing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace AGVControl.Models
{
public class RFIDPoint
{
public Point Location { get; set; }
public ushort Value { get; set; }
public string NextRFID { get; set; } // 다음 RFID 포인트의 값
public bool IsBidirectional { get; set; } // 양방향 연결 여부
public bool IsRotatable { get; set; } // 회전 가능 여부
public AgvDir? FixedDirection { get; set; } // 고정 방향(없으면 null)
public bool IsTerminal { get; set; } // 종단 여부
public bool NeedTurn { get; set; }
public bool TurnStop { get; set; }
public bool TurnOK { get; set; }
public DateTime TurnStart { get; set; }
public DateTime TurnEnd { get; set; }
[Browsable(false)]
public RectangleF Bounds { get; set; }
public void Clear()
{
this.Location = Point.Empty;
this.Value = 0;
TurnStop = false;
TurnOK = false;
TurnStart = new DateTime(1982, 11, 23);
TurnEnd = new DateTime(1982, 11, 23);
}
public bool IsEmpty
{
get
{
//RFID값이나 위치 값이 없으면 비어있는 것으로 한다.
if (this.Location.IsEmpty) return true;
if ((this.Value < 1)) return true;
return false;
}
}
public RFIDPoint()
{
IsRotatable = false; // 기본값은 회전 불가능
IsBidirectional = true; // 기본값은 양방향
FixedDirection = null;
IsTerminal = false; // 기본값은 종단 아님
Clear();
}
public override string ToString()
{
return $"[RFIDPoint] {Value},P:{Location.X},{Location.Y}";
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
namespace AGVControl.Models
{
public class ToolBarItem
{
private bool _ishovering = false;
public int Idx { get; set; }
public Rectangle Bounds { get; set; }
public bool isHovering
{
get { return _ishovering; }
set
{
Dirty = _ishovering != value;
_ishovering = value;
}
}
public string Title { get; set; }
public bool Dirty { get; private set; }
public ToolBarItem()
{
Bounds = Rectangle.Empty;
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AGVControl
{
/// <summary>
/// 실제 AGV컨트롤러의 STS값에는 F,B의 값이 들어있고
/// 아래 항목은 해당 문자의 ASCII코드값이다 첫자를 byte로 변경하고 변환하면 된다
/// </summary>
public enum AgvDir : byte
{
Forward = 0x46,
Backward = 0x42,
}
public enum AGVMoveState
{
Stop = 0,
Run
}
/// <summary>
/// 실제 AGV컨트롤러의 STS값에는 H,L,M,S의 값이 들어있고
/// 아래 항목은 해당 문자의 ASCII코드값이다 첫자를 byte로 변경하고 변환하면 된다
/// </summary>
public enum AgvSpeed : byte
{
High = 0x48,
Middle = 0x4D,
Low = 0x4C,
MarkStop = 0x53,
}
/// <summary>
/// STS : S,L,R
/// </summary>
public enum AgvSts : byte
{
Straight = 0x53,
Left = 0x4c,
Right = 0x052,
}
public enum AGVActionReasonCode
{
Unknown = 0,
NoPosition, // 위치 미확정(처음 기동)
NoPath, // 경로 없음 또는 현재 위치 미확정
NotOnPath, // 현재 위치가 경로에 없음
Arrived, // 경로의 마지막 지점(목적지 도달)
Normal, // 정상(다음 RFID 있음)
/// <summary>
/// 방향전환을 위한 턴이다
/// </summary>
NeedTurnMove,
/// <summary>
/// 지정된 RFID위치에서 TURN이 요청되었다
/// </summary>
NeedTurnPoint,
NoTurnPoint,
PathCalcError,
NoDirection,
MoveForTurn,
busy,
WaitForMarkStop,
}
}

View File

@@ -0,0 +1,37 @@
namespace AGVControl
{
partial class MyRadioButton
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AGVControl
{
public partial class MyRadioButton : RadioButton
{
public int CheckWidth { get; set; } = 30;
public Color CheckOnColor { get; set; } = Color.OrangeRed;
public Color CheckOffColor { get; set; } = Color.DimGray;
public Color Bordercolor { get; set; } = Color.DimGray;
public int BorderSize { get; set; } = 2;
public int BorderRadius { get; set; } = 7;
public MyRadioButton()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.Resize += arLabel_Resize;
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.InterpolationMode = InterpolationMode.High;
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
pe.Graphics.Clear(this.BackColor);
var XPosition = this.DisplayRectangle.Left;
var YPosition = this.DisplayRectangle.Top;
using (GraphicsPath Path = new GraphicsPath())
{
Path.AddLine(XPosition + BorderRadius, YPosition, XPosition + Width - (BorderRadius * 2), YPosition);
Path.AddArc(XPosition + Width - (BorderRadius * 2), YPosition, BorderRadius * 2, BorderRadius * 2, 270, 90);
Path.AddLine(XPosition + Width, YPosition + BorderRadius, XPosition + Width, YPosition + Height - (BorderRadius * 2));
Path.AddArc(XPosition + Width - (BorderRadius * 2), YPosition + Height - (BorderRadius * 2), BorderRadius * 2, BorderRadius * 2, 0, 90);
Path.AddLine(XPosition + Width - (BorderRadius * 2), YPosition + Height, XPosition + BorderRadius, YPosition + Height);
Path.AddArc(XPosition, YPosition + Height - (BorderRadius * 2), BorderRadius * 2, BorderRadius * 2, 90, 90);
Path.AddLine(XPosition, YPosition + Height - (BorderRadius * 2), XPosition, YPosition + BorderRadius);
Path.AddArc(XPosition, YPosition, BorderRadius * 2, BorderRadius * 2, 180, 90);
Path.CloseFigure();
var r1 = new Rectangle(
DisplayRectangle.Left, DisplayRectangle.Top,
CheckWidth,
DisplayRectangle.Height - 1);
var r2 = new Rectangle(r1.Right + 3, r1.Top, DisplayRectangle.Width - r1.Width - 3 - Padding.Right, r1.Height);
var CC = Checked ? CheckOnColor : CheckOffColor;
pe.Graphics.FillRectangle(new SolidBrush(CC), r1);
StringFormat sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
if (this.TextAlign == ContentAlignment.MiddleLeft) sf.Alignment = StringAlignment.Near;
else if (this.TextAlign == ContentAlignment.MiddleRight) sf.Alignment = StringAlignment.Far;
pe.Graphics.DrawString(this.Text, this.Font, new SolidBrush(ForeColor), r2, sf);
//pe.Graphics.DrawRectangle(new Pen(this.Bordercolor, this.BorderSize), DisplayRectangle);
this.Region = new System.Drawing.Region(Path);
pe.Graphics.DrawPath(new Pen(this.Bordercolor, this.BorderSize), Path);
}
}
void arLabel_Resize(object sender, EventArgs e)
{
Invalidate();
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
// 이러한 특성 값을 변경하세요.
[assembly: AssemblyTitle("AGVControl")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AGVControl")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
[assembly: ComVisible(false)]
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
[assembly: Guid("8cb883c0-99c3-4dd4-b017-f9b92010a806")]
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
//
// 주 버전
// 부 버전
// 빌드 번호
// 수정 버전
//
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
// 기본값으로 할 수 있습니다.
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
// 런타임 버전:4.0.30319.42000
//
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AGVControl.Properties {
using System;
/// <summary>
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
/// </summary>
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
// 클래스에서 자동으로 생성되었습니다.
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을
// 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AGVControl.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대한 현재 스레드의 CurrentUICulture
/// 속성을 재정의합니다.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// System.Drawing.Bitmap 형식의 지역화된 리소스를 찾습니다.
/// </summary>
internal static System.Drawing.Bitmap ico_navi_40 {
get {
object obj = ResourceManager.GetObject("ico_navi_40", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ico_navi_40" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ico_navi_40.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -0,0 +1,37 @@
namespace AGVControl
{
partial class RoundButton
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AGVControl
{
public partial class RoundButton : Button
{
public int CornerRadius { get; set; } = 7;
public RoundButton()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.Resize += arLabel_Resize;
}
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.InterpolationMode = InterpolationMode.High;
pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pevent.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
var XPosition = this.DisplayRectangle.Left;
var YPosition = this.DisplayRectangle.Top;
using (GraphicsPath Path = new GraphicsPath())
{
Path.AddLine(XPosition + CornerRadius, YPosition, XPosition + Width - (CornerRadius * 2), YPosition);
Path.AddArc(XPosition + Width - (CornerRadius * 2), YPosition, CornerRadius * 2, CornerRadius * 2, 270, 90);
Path.AddLine(XPosition + Width, YPosition + CornerRadius, XPosition + Width, YPosition + Height - (CornerRadius * 2));
Path.AddArc(XPosition + Width - (CornerRadius * 2), YPosition + Height - (CornerRadius * 2), CornerRadius * 2, CornerRadius * 2, 0, 90);
Path.AddLine(XPosition + Width - (CornerRadius * 2), YPosition + Height, XPosition + CornerRadius, YPosition + Height);
Path.AddArc(XPosition, YPosition + Height - (CornerRadius * 2), CornerRadius * 2, CornerRadius * 2, 90, 90);
Path.AddLine(XPosition, YPosition + Height - (CornerRadius * 2), XPosition, YPosition + CornerRadius);
Path.AddArc(XPosition, YPosition, CornerRadius * 2, CornerRadius * 2, 180, 90);
Path.CloseFigure();
this.Region = new System.Drawing.Region(Path);
}
base.OnPaint(pevent);
}
void arLabel_Resize(object sender, EventArgs e)
{
Invalidate();
}
}
}

View File

@@ -0,0 +1,36 @@
namespace AGVControl
{
partial class ValueSelect
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -0,0 +1,262 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Windows.Forms;
namespace AGVControl
{
public partial class ValueSelect : Control
{
public ValueSelect()
{
InitializeComponent();
// Set Optimized Double Buffer to reduce flickering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
// Redraw when resized
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.Resize += arLabel_Resize;
this.MouseClick += ValueSelect_MouseClick;
this.MouseEnter += (s1, e1) => { this.Cursor = Cursors.Hand; };
this.MouseLeave += (s1, e1) => { this.Cursor = Cursors.Default; };
}
private void ValueSelect_MouseClick(object sender, MouseEventArgs e)
{
//마우스클릭
if (rectLButton.IsEmpty == false && rectLButton.Contains(e.Location))
{
if (SideButtonClickValue != 0.0)
{
var newvalue = this.Value - SideButtonClickValue;
if (newvalue < MinValue) newvalue = MinValue;
this.Value = newvalue;
}
if (ButtonClick != null) ButtonClick(this, new ButtonClickEventArgs(MouseButtons.Left));
}
if (rectRButton.IsEmpty == false && rectRButton.Contains(e.Location))
{
if (SideButtonClickValue != 0.0)
{
var newvalue = this.Value + SideButtonClickValue;
if (newvalue > MaxValue) newvalue = MaxValue;
this.Value = newvalue;
}
if (ButtonClick != null) ButtonClick(this, new ButtonClickEventArgs(MouseButtons.Right));
}
if (rectCButton.IsEmpty == false && rectCButton.Contains(e.Location))
if (ButtonClick != null) ButtonClick(this, new ButtonClickEventArgs(MouseButtons.Middle));
}
private Rectangle rectLButton;
private Rectangle rectRButton;
private Rectangle rectCButton;
private Boolean bMakeRect = true;
private double _minvalue = 0.0;
private double _maxvalue = 9999999999.0;
public double MinValue { get { return _minvalue; } set { _minvalue = value; Invalidate(); } }
public double MaxValue { get { return _maxvalue; } set { _maxvalue = value; Invalidate(); } }
private UInt16 _decimalposition = 0;
private string _buttonwdith = string.Empty;
private double _value = 0;
private Color _border = Color.Lime;
private double buttongabp;
public double SideButtonClickValue { get { return buttongabp; } set { buttongabp = value; Invalidate(); } }
private Color _backbutton = Color.White;
private Color _sidebutto = Color.Black;
public Color BackColorButton { get { return _backbutton; } set { _backbutton = value; Invalidate(); } }
public Color ForeColorButton { get { return _sidebutto; } set { _sidebutto = value; Invalidate(); } }
public Color ColorBorder { get { return _border; } set { _border = value; Invalidate(); } }
public override string Text
{
get => Value.ToString();
set
{
if (double.TryParse(value, out double dblval) == true)
this.Value = dblval;
}
}
public double Value
{
get { return _value; }
set
{
if (_value != value)
{
//이 값이 범위를 초과했는지 확인
if (value < MinValue)
{
_value = MinValue;
//throw new Exception(string.Format("입력값이 컨트롤의 최소값보다 작습니다(입력:{0},최소값:{1})",value,MinValue));
}
else if (value > MaxValue)
{
_value = MaxValue;
//throw new Exception(string.Format("입력값이 컨트롤의 최대값보다 작습니다(입력:{0},최대값:{1})", value, MaxValue));
}
else
{
_value = value;
}
Invalidate();
if (ValueChanged != null) ValueChanged(this, new EventArgs());
}
}
}
public UInt16 DecimalPosition { get { return _decimalposition; } set { _decimalposition = value; Invalidate(); } }
public string ButtonWidth { get { return _buttonwdith; } set { _buttonwdith = value; Invalidate(); } }
private string _nulldisplay = string.Empty;
public string NullDisplay { get { return _nulldisplay; } set { _nulldisplay = value; Invalidate(); } }
private Font _sidefont;
public Font FontSideButton { get { if (_sidefont == null) return this.Font; else return _sidefont; } set { _sidefont = value; Invalidate(); } }
void arLabel_Resize(object sender, EventArgs e)
{
this.bMakeRect = true;
Invalidate();
}
public class ButtonClickEventArgs : EventArgs
{
public MouseButtons Button;
public ButtonClickEventArgs(MouseButtons button)
{
this.Button = button;
// Console.WriteLine("button clickc : " +button.ToString());
}
}
public event EventHandler<ButtonClickEventArgs> ButtonClick;
public event EventHandler ValueChanged;
protected override void OnPaint(PaintEventArgs pe)
{
//base.OnPaint(pe);
if (bMakeRect) MakeRect();
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
sf.Trimming = StringTrimming.None;
sf.FormatFlags = StringFormatFlags.NoWrap;
//좌측버튼표시
if (rectLButton.IsEmpty == false)
{
pe.Graphics.FillRectangle(new SolidBrush(BackColorButton), rectLButton);
pe.Graphics.DrawString("<<", this.FontSideButton, new SolidBrush(this.ForeColorButton), rectLButton, sf);
}
if (rectRButton.IsEmpty == false)
{
pe.Graphics.FillRectangle(new SolidBrush(BackColorButton), rectRButton);
pe.Graphics.DrawString(">>", this.FontSideButton, new SolidBrush(this.ForeColorButton), rectRButton, sf);
}
//값표시
string valuestr;
if (Value == 0.0 && string.IsNullOrEmpty(NullDisplay) == false)
valuestr = NullDisplay;
else
valuestr = Value.ToString("N" + DecimalPosition.ToString());
pe.Graphics.FillRectangle(new SolidBrush(BackColor), rectCButton);
pe.Graphics.DrawString(valuestr, this.Font, new SolidBrush(this.ForeColor), rectCButton, sf);
sf.Dispose();
//테두리표시
if (rectLButton.IsEmpty == false)
pe.Graphics.DrawRectangle(new Pen(ColorBorder), rectLButton);
if (rectRButton.IsEmpty == false)
pe.Graphics.DrawRectangle(new Pen(ColorBorder), rectRButton);
pe.Graphics.DrawRectangle(new Pen(ColorBorder), rectCButton);
}
void MakeRect()
{
int bWidth;
if (string.IsNullOrEmpty(ButtonWidth) == false)
{
if (ButtonWidth.EndsWith("%"))
{
if (int.TryParse(ButtonWidth.Substring(0, ButtonWidth.Length - 1), out bWidth) == false)
bWidth = 0; //숫자로 바꾸는거 실패
else bWidth = (int)(this.DisplayRectangle.Width * (bWidth / 100.0));
}
else
{
if (int.TryParse(ButtonWidth, out bWidth) == false)
bWidth = 0;
}
}
else bWidth = 0;
if (bWidth > 0)
{
int buttongap = 1;
//각버튼간 2px 간격을 띄운다.
bWidth = bWidth - buttongap * 2;
rectLButton = new Rectangle(
DisplayRectangle.Left,
DisplayRectangle.Top,
bWidth,
DisplayRectangle.Height - 1);
rectCButton = new Rectangle(
rectLButton.Right + buttongap,
DisplayRectangle.Top,
DisplayRectangle.Width - rectLButton.Width * 2 - buttongap * 2,
DisplayRectangle.Height - 1);
rectRButton = new Rectangle(
rectCButton.Right + buttongap,
DisplayRectangle.Top,
bWidth - 1,
DisplayRectangle.Height - 1);
}
else
{
rectLButton = Rectangle.Empty;
rectRButton = Rectangle.Empty;
rectCButton = new Rectangle(DisplayRectangle.Left,
DisplayRectangle.Top,
DisplayRectangle.Width - 1,
DisplayRectangle.Height - 1);
}
bMakeRect = true;
}
}
}

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8CB883C0-99C3-4DD4-B017-F9B92010A806}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AGVControl</RootNamespace>
<AssemblyName>AGVControl</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="arCommUtil">
<HintPath>..\..\..\DLL\arCommUtil.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Dialog\fMapDesign.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Dialog\fMapDesign.Designer.cs">
<DependentUpon>fMapDesign.cs</DependentUpon>
</Compile>
<Compile Include="Dialog\fPropertyRFIDPoint.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Dialog\fPropertyRFIDPoint.Designer.cs">
<DependentUpon>fPropertyRFIDPoint.cs</DependentUpon>
</Compile>
<Compile Include="GridView\ColorListItem.cs" />
<Compile Include="GridView\GridItem.cs" />
<Compile Include="GridView\GridView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="GridView\GridView.Designer.cs">
<DependentUpon>GridView.cs</DependentUpon>
</Compile>
<Compile Include="MapControlManager.cs" />
<Compile Include="Models\AGVActionPrediction.cs" />
<Compile Include="BatteryLevelGauge.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="BatteryLevelGauge.Designer.cs">
<DependentUpon>BatteryLevelGauge.cs</DependentUpon>
</Compile>
<Compile Include="GuideSensor.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="GuideSensor.Designer.cs">
<DependentUpon>GuideSensor.cs</DependentUpon>
</Compile>
<Compile Include="MapControl.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MapControl.Designer.cs">
<DependentUpon>MapControl.cs</DependentUpon>
</Compile>
<Compile Include="Models\AGV.cs" />
<Compile Include="Models\CustomLine.cs" />
<Compile Include="Models\enumStruct.cs" />
<Compile Include="Models\MagnetLine.cs" />
<Compile Include="Models\MapElements.cs" />
<Compile Include="Models\MapText.cs" />
<Compile Include="Models\PathResult.cs" />
<Compile Include="Models\RFIDLine.cs" />
<Compile Include="Models\RFIDPoint.cs" />
<Compile Include="Models\ToolBarItem.cs" />
<Compile Include="MyRadioButton.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MyRadioButton.Designer.cs">
<DependentUpon>MyRadioButton.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Models\RFIDConnection.cs" />
<Compile Include="RoundButton.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="RoundButton.Designer.cs">
<DependentUpon>RoundButton.cs</DependentUpon>
</Compile>
<Compile Include="ValueSelect.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ValueSelect.Designer.cs">
<DependentUpon>ValueSelect.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Dialog\fMapDesign.resx">
<DependentUpon>fMapDesign.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Dialog\fPropertyRFIDPoint.resx">
<DependentUpon>fPropertyRFIDPoint.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MapControl.resx">
<DependentUpon>MapControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommData\CommData.csproj">
<Project>{14e8c9a5-013e-49ba-b435-efefc77dd623}</Project>
<Name>CommData</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources\ico_navi_40.png" />
<None Include="sample.route" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,105 @@
[RFID_POINTS]
100,486,517
173,488,516
230,493,515
309,489,514
370,490,513
437,487,512
483,459,511
511,421,510
543,371,509
569,329,508
608,289,507
661,279,506
701,297,505
698,349,504
698,391,503
699,449,502
691,491,501
570,275,400
517,264,401
454,264,402
388,262,403
315,258,404
639,234,600
621,182,601
641,183,602
657,101,603
627,82,604
560,73,605
499,65,606
432,65,607
264,232,405
363,60,608
654,508,500
96,542,100
159,542,101
226,542,102
309,541,103
369,542,104
483,165,753
735,163,700
523,170,752
565,175,751
597,182,750
665,181,703
700,176,702
722,170,701
[RFID_LINES]
100,486,173,488,517,516,False,73.02739
173,488,230,493,516,515,False,57.21888
230,493,309,489,515,514,False,79.1012
309,489,370,490,514,513,False,61.0082
370,490,437,487,513,512,False,67.06713
437,487,483,459,512,511,False,53.85165
483,459,511,421,511,510,False,47.20169
511,421,543,371,510,509,False,59.36329
543,371,569,329,509,508,False,49.39635
569,329,608,289,508,507,False,55.86591
608,289,639,234,507,600,False,63.13478
639,234,665,181,600,703,False,59.03389
665,181,657,101,703,603,False,80.399
657,101,627,82,603,604,False,35.51056
627,82,560,73,604,605,False,67.60178
560,73,499,65,605,606,False,61.52235
499,65,432,65,606,607,False,67
432,65,363,60,607,608,False,69.18092
641,183,665,181,602,703,False,24.08319
665,181,700,176,703,702,False,35.35534
700,176,722,170,702,701,False,22.80351
722,170,735,163,701,700,False,14.76482
641,183,621,182,602,601,False,20.02498
621,182,597,182,601,750,False,24
597,182,565,175,750,751,False,32.75668
565,175,523,170,751,752,False,42.29657
523,170,483,165,752,753,False,40.31129
264,232,315,258,405,404,False,57.24509
315,258,388,262,404,403,False,73.1095
388,262,454,264,403,402,False,66.0303
454,264,517,264,402,401,False,63
517,264,570,275,401,400,False,54.12947
570,275,608,289,400,507,False,40.49691
608,289,661,279,507,506,False,53.93515
661,279,701,297,506,505,False,43.86343
701,297,698,349,505,504,False,52.08647
698,349,698,391,504,503,False,42
698,391,699,449,503,502,False,58.00862
699,449,691,491,502,501,False,42.75512
691,491,654,508,501,500,False,40.71855
96,542,100,486,100,517,False,56.14267
159,542,173,488,101,516,False,55.7853
226,542,230,493,102,515,False,49.16299
309,541,309,489,103,514,False,52
369,542,370,490,104,513,False,52.00961
[MAP_TEXTS]
179,251,-1,-16777216,Arial,12,OPS-2
239,52,-256,-65408,Arial,12,SSOTRON-3
617,527,-16711936,-16777216,Arial,12,SSOTRON-1
87,551,-16777216,16777215,Arial,12,B1
150,550,-16777216,16777215,Arial,12,B2
227,553,-16777216,16777215,Arial,12,B3
299,555,-16777216,16777215,Arial,12,B4
367,554,-16777216,16777215,Arial,12,B5
453,128,-16777216,-8323073,Arial,12,CHG1
725,124,-16777216,-8323073,Arial,12,CHG2
[CUSTOM_LINES]