Compare commits
40 Commits
455e18f427
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
649d87cae3 | ||
|
|
b3f661969a | ||
|
|
4f162e50b5 | ||
|
|
35df73fd29 | ||
|
|
8499c1c5be | ||
|
|
3408e3fc30 | ||
|
|
5a4c73e4df | ||
|
|
34d1bdf504 | ||
|
|
3cae423736 | ||
|
|
d777adc219 | ||
|
|
b62cd5f52e | ||
|
|
32217c8501 | ||
|
|
9274727fa9 | ||
| 2a44ba28a8 | |||
| 4bdc36040d | |||
| 384f2affcb | |||
|
|
51579591a2 | ||
|
|
cef2fa8095 | ||
|
|
1f37871336 | ||
| eb0e08d290 | |||
|
|
4153362588 | ||
| 67a48531ad | |||
|
|
a7f938ff19 | ||
|
|
9db88e5d6b | ||
| a8cb952ea4 | |||
| 3c8eae889c | |||
| 764fbbd204 | |||
| 34b038c4be | |||
| 703e1387bf | |||
| f507a6487e | |||
|
|
4e9d29d22f | ||
|
|
07ddc0425f | ||
|
|
ee2a6f04bb | ||
|
|
6024f372d3 | ||
|
|
9a0a389e07 | ||
|
|
af280d7b27 | ||
|
|
868fa2deec | ||
|
|
3695ab0044 | ||
|
|
2236e3b1ba | ||
|
|
9db031c305 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
obj
|
obj
|
||||||
bin
|
bin
|
||||||
|
.vscode
|
||||||
*.user
|
*.user
|
||||||
*.v12
|
*.v12
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
140
AGVEmulator/AGVEmulator.csproj
Normal file
140
AGVEmulator/AGVEmulator.csproj
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?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>{9312AB43-72F6-4365-A266-E767215FA7F5}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>AGVEmulator</RootNamespace>
|
||||||
|
<AssemblyName>AGVEmulator</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<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' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<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="arControl.Net4">
|
||||||
|
<HintPath>..\Cs_HMI\DLL\arControl.Net4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ArLog.Net4">
|
||||||
|
<HintPath>..\Cs_HMI\DLL\ArLog.Net4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ArSetting.Net4">
|
||||||
|
<HintPath>..\Cs_HMI\DLL\ArSetting.Net4.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Deployment" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="DevAGV.cs" />
|
||||||
|
<Compile Include="DevBMS.cs" />
|
||||||
|
<Compile Include="DevXBE.cs" />
|
||||||
|
<Compile Include="fMain.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="fMain.Designer.cs">
|
||||||
|
<DependentUpon>fMain.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="RS232.cs" />
|
||||||
|
<Compile Include="RunCode\_AGV.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="RunCode\_XBEE.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="RunCode\_BMS.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UC\SerialConn.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UC\SerialConn.Designer.cs">
|
||||||
|
<DependentUpon>SerialConn.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UC\AgvViewer.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UC\AgvViewer.Designer.cs">
|
||||||
|
<DependentUpon>AgvViewer.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="fMain.resx">
|
||||||
|
<DependentUpon>fMain.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="UC\SerialConn.resx">
|
||||||
|
<DependentUpon>SerialConn.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="app.config" />
|
||||||
|
<None Include="Sample\packetFD.bin">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Include="Sample\packetFC.bin">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Cs_HMI\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj">
|
||||||
|
<Project>{c5f7a8b2-8d3e-4a1b-9c6e-7f4d5e2a9b1c}</Project>
|
||||||
|
<Name>AGVNavigationCore</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Cs_HMI\SubProject\CommUtil\arCommUtil.csproj">
|
||||||
|
<Project>{14e8c9a5-013e-49ba-b435-ffffff7dd623}</Project>
|
||||||
|
<Name>arCommUtil</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Cs_HMI\SubProject\EnigProtocol\enigprotocol\ENIGProtocol.csproj">
|
||||||
|
<Project>{9365803b-933d-4237-93c7-b502c855a71c}</Project>
|
||||||
|
<Name>ENIGProtocol</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
43
AGVEmulator/AGVEmulator.sln
Normal file
43
AGVEmulator/AGVEmulator.sln
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.14.36804.6 d17.14
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVEmulator", "AGVEmulator.csproj", "{9312AB43-72F6-4365-A266-E767215FA7F5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "..\Cs_HMI\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj", "{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "..\Cs_HMI\SubProject\ENIGProtocol\enigprotocol\ENIGProtocol.csproj", "{9365803B-933D-4237-93C7-B502C855A71C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "..\Cs_HMI\SubProject\CommUtil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{9312AB43-72F6-4365-A266-E767215FA7F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9312AB43-72F6-4365-A266-E767215FA7F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9312AB43-72F6-4365-A266-E767215FA7F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9312AB43-72F6-4365-A266-E767215FA7F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {C353381B-16D4-4298-A97E-6DE0FC4A66B2}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
537
AGVEmulator/DevAGV.cs
Normal file
537
AGVEmulator/DevAGV.cs
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
using arCtl;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
class DevAGV : AR.Dev.RS232
|
||||||
|
{
|
||||||
|
//######### private variable
|
||||||
|
byte runtime = 0;
|
||||||
|
|
||||||
|
//######### public variable
|
||||||
|
public UInt16 system0 = 0;
|
||||||
|
public UInt16 system1 = 0;
|
||||||
|
public UInt16 error = 0;
|
||||||
|
public byte signal = 0;
|
||||||
|
public char sts_bunki = 'S';
|
||||||
|
public char sts_speed = 'L';
|
||||||
|
public char sts_dir = 'F';
|
||||||
|
public char sts_sensor = '1';
|
||||||
|
|
||||||
|
public enum eerror
|
||||||
|
{
|
||||||
|
Emergency = 0,
|
||||||
|
Overcurrent,
|
||||||
|
Charger_run_error,
|
||||||
|
Charger_pos_error,
|
||||||
|
line_out_error = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 기동시 자석 감지 에러
|
||||||
|
/// </summary>
|
||||||
|
runerror_by_no_magent_line,
|
||||||
|
/// <summary>
|
||||||
|
/// 호출제어기 통신 오류
|
||||||
|
/// </summary>
|
||||||
|
controller_comm_error = 11,
|
||||||
|
/// <summary>
|
||||||
|
/// 도착경보기 통신 오류
|
||||||
|
/// </summary>
|
||||||
|
arrive_ctl_comm_error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 자동문제어기 통신 오류
|
||||||
|
/// </summary>
|
||||||
|
door_ctl_comm_error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 자동충전기 통신 오류
|
||||||
|
/// </summary>
|
||||||
|
charger_comm_error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 교차로 제어기 통신 오류
|
||||||
|
/// </summary>
|
||||||
|
cross_ctrl_comm_error,
|
||||||
|
}
|
||||||
|
public enum esignal
|
||||||
|
{
|
||||||
|
front_gate_out = 0,
|
||||||
|
rear_sensor_out,
|
||||||
|
mark_sensor_1,
|
||||||
|
mark_sensor_2,
|
||||||
|
front_left_sensor,
|
||||||
|
front_right_sensor,
|
||||||
|
front_center_sensor,
|
||||||
|
charger_align_sensor,
|
||||||
|
}
|
||||||
|
public enum esystemflag0
|
||||||
|
{
|
||||||
|
Memory_RW_State = 5,
|
||||||
|
EXT_IO_Conn_State,
|
||||||
|
RFID_Conn_State,
|
||||||
|
M5E_Module_Run_State = 8,
|
||||||
|
Front_Ultrasonic_Conn_State,
|
||||||
|
Front_Untrasonic_Sensor_State,
|
||||||
|
Side_Ultrasonic_Conn_State,
|
||||||
|
Side_Ultrasonic_Sensor_State = 12,
|
||||||
|
Front_Guide_Sensor_State,
|
||||||
|
Rear_Guide_Sensor_State,
|
||||||
|
Battery_Level_Check
|
||||||
|
}
|
||||||
|
public enum esystemflag1
|
||||||
|
{
|
||||||
|
Side_Detect_Ignore = 3,
|
||||||
|
Melody_check,
|
||||||
|
Mark2_check,
|
||||||
|
Mark1_check,
|
||||||
|
gateout_check,
|
||||||
|
Battery_charging = 8,
|
||||||
|
re_Start,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 전방 감지 무시
|
||||||
|
/// </summary>
|
||||||
|
front_detect_ignore,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 전방장애물감지상태
|
||||||
|
/// </summary>
|
||||||
|
front_detect_check,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 전방감지 후 정지 상태
|
||||||
|
/// </summary>
|
||||||
|
stop_by_front_detect = 12,
|
||||||
|
/// <summary>
|
||||||
|
/// 교차로 진입 후 정지 상태
|
||||||
|
/// </summary>
|
||||||
|
stop_by_cross_in,
|
||||||
|
agv_stop,
|
||||||
|
agv_run
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum evaluetype
|
||||||
|
{
|
||||||
|
system0,
|
||||||
|
system1,
|
||||||
|
error,
|
||||||
|
signal,
|
||||||
|
}
|
||||||
|
public enum estsvaluetype
|
||||||
|
{
|
||||||
|
bunki,
|
||||||
|
speed,
|
||||||
|
direction,
|
||||||
|
sensor
|
||||||
|
}
|
||||||
|
public class ValueChangedArgs : EventArgs
|
||||||
|
{
|
||||||
|
public int Idx { get; set; }
|
||||||
|
public bool Value { get; set; }
|
||||||
|
public evaluetype vtype { get; set; }
|
||||||
|
public ValueChangedArgs(int idx, bool val, evaluetype isOut)
|
||||||
|
{
|
||||||
|
this.Idx = idx;
|
||||||
|
this.Value = val;
|
||||||
|
this.vtype = isOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class StsValueChangedArgs : EventArgs
|
||||||
|
{
|
||||||
|
public char Value { get; set; }
|
||||||
|
public estsvaluetype vtype { get; set; }
|
||||||
|
public StsValueChangedArgs(estsvaluetype vType, char val)
|
||||||
|
{
|
||||||
|
this.Value = val;
|
||||||
|
this.vtype = vType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RequestStatusDataArgs : EventArgs
|
||||||
|
{
|
||||||
|
public UInt16 system0 { get; set; } = 0;
|
||||||
|
public UInt16 system1 { get; set; } = 0;
|
||||||
|
public char speed { get; set; } = 'L';
|
||||||
|
public char direction { get; set; } = 'F';
|
||||||
|
public char bunki { get; set; } = 'S';
|
||||||
|
|
||||||
|
public byte signal { get; set; } = 0;
|
||||||
|
public float volt { get; set; } = 0f;
|
||||||
|
public UInt16 error { get; set; } = 0;
|
||||||
|
public char sensor { get; set; } = '0';
|
||||||
|
public RequestStatusDataArgs()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public event EventHandler<RequestStatusDataArgs> RequestStatusData;
|
||||||
|
public event EventHandler<ValueChangedArgs> ValueChanged;
|
||||||
|
public event EventHandler<StsValueChangedArgs> StsValueChanged;
|
||||||
|
|
||||||
|
public class commandargs : EventArgs
|
||||||
|
{
|
||||||
|
public string Command { get; set; }
|
||||||
|
public commandargs(string cmd)
|
||||||
|
{
|
||||||
|
this.Command = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public event EventHandler<commandargs> Command;
|
||||||
|
public class FrameData
|
||||||
|
{
|
||||||
|
public string Checksum { get; set; }
|
||||||
|
public byte stx { get; set; }
|
||||||
|
public byte etx { get; set; }
|
||||||
|
public string cmd { get; set; }
|
||||||
|
public string data { get; set; }
|
||||||
|
public FrameData(byte[] data)
|
||||||
|
{
|
||||||
|
this.stx = data[0];
|
||||||
|
this.etx = data[data.Length - 1];
|
||||||
|
this.Checksum = System.Text.Encoding.Default.GetString(data, data.Length - 3, 2);
|
||||||
|
this.data = System.Text.Encoding.Default.GetString(data, 1, data.Length - 4);
|
||||||
|
this.cmd = this.data.Substring(0, 3);
|
||||||
|
this.data = this.data.Substring(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region SetAGV/SetSTS/SetBIT/GetBIT
|
||||||
|
Boolean GetBit(ref UInt16 _value, int idx)
|
||||||
|
{
|
||||||
|
var offset = (UInt16)(1 << idx);
|
||||||
|
return (_value & offset) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean GetBit(ref byte _value, int idx)
|
||||||
|
{
|
||||||
|
var offset = (byte)(1 << idx);
|
||||||
|
return (_value & offset) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetBit(ref UInt16 _value, int idx, Boolean value)
|
||||||
|
{
|
||||||
|
var oldvalue = GetBit(ref _value, idx);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
var offset = (UInt16)(1 << idx);
|
||||||
|
_value = (UInt16)(_value | offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var offset = (UInt16)(~(1 << idx));
|
||||||
|
_value = (UInt16)(_value & offset);
|
||||||
|
}
|
||||||
|
return oldvalue != value;
|
||||||
|
}
|
||||||
|
bool SetBit(ref byte _value, int idx, Boolean value)
|
||||||
|
{
|
||||||
|
var oldvalue = GetBit(ref _value, idx);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
var offset = (byte)(1 << idx);
|
||||||
|
_value = (byte)(_value | offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var offset = (byte)(~(1 << idx));
|
||||||
|
_value = (byte)(_value & offset);
|
||||||
|
}
|
||||||
|
return oldvalue != value;
|
||||||
|
}
|
||||||
|
public bool GetAGV(DevAGV.esystemflag1 flag)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
return GetBit(ref system1, idx);
|
||||||
|
}
|
||||||
|
public void SetAGV(DevAGV.esystemflag0 flag, bool value)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
if (SetBit(ref system0, idx, value))
|
||||||
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.system0));
|
||||||
|
}
|
||||||
|
public void SetAGV(DevAGV.esystemflag1 flag, bool value)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
if (SetBit(ref system1, idx, value))
|
||||||
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.system1));
|
||||||
|
}
|
||||||
|
public void SetAGV(DevAGV.eerror flag, bool value)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
if (SetBit(ref error, idx, value))
|
||||||
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.error));
|
||||||
|
}
|
||||||
|
public void SetAGV(DevAGV.esignal flag, bool value)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
if (SetBit(ref signal, idx, value))
|
||||||
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.signal));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSTS(estsvaluetype target, char value)
|
||||||
|
{
|
||||||
|
//bool changed = false;
|
||||||
|
switch (target)
|
||||||
|
{
|
||||||
|
case estsvaluetype.sensor:
|
||||||
|
//changed = this.sts_sensor.Equals(value);
|
||||||
|
sts_sensor = value;
|
||||||
|
break;
|
||||||
|
case estsvaluetype.direction:
|
||||||
|
//changed = this.sts_dir.Equals(value);
|
||||||
|
sts_dir = value;
|
||||||
|
break;
|
||||||
|
case estsvaluetype.speed:
|
||||||
|
//changed = this.sts_speed.Equals(value);
|
||||||
|
sts_speed = value;
|
||||||
|
break;
|
||||||
|
case estsvaluetype.bunki:
|
||||||
|
//changed = this.sts_bunki.Equals(value);
|
||||||
|
sts_bunki = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//if(changed)
|
||||||
|
{
|
||||||
|
StsValueChanged?.Invoke(this, new StsValueChangedArgs(target, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override bool ProcessRecvData(byte[] data)
|
||||||
|
{
|
||||||
|
//dd로 시작하고 34개의 데이터
|
||||||
|
//STS258FFFF40000000LSF0000003A
|
||||||
|
//02 53 54 53 32 35 38 46 46 46 46 34 30 30 30 30 30 30 30 4C 53 46 30 30 30 30 30 30 33 41 03
|
||||||
|
var frame = new FrameData(data);
|
||||||
|
|
||||||
|
switch (frame.cmd)
|
||||||
|
{
|
||||||
|
case "CRN": //기동명령
|
||||||
|
//sts_dir = frame.data[0];
|
||||||
|
SetSTS(estsvaluetype.direction, frame.data[0]);
|
||||||
|
SetAGV(esystemflag1.agv_stop, false);
|
||||||
|
SetAGV(esystemflag1.agv_run, true);
|
||||||
|
break;
|
||||||
|
case "CST": //중지명령
|
||||||
|
//(바로 중지한다)
|
||||||
|
if (frame.data.StartsWith("M"))
|
||||||
|
{
|
||||||
|
Command?.Invoke(this, new commandargs("stopmark"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetAGV(esystemflag1.agv_run, false);
|
||||||
|
SetAGV(esystemflag1.agv_stop, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "CBR": //분기명령
|
||||||
|
//FSL
|
||||||
|
SetSTS(estsvaluetype.direction, frame.data[0]);
|
||||||
|
SetSTS(estsvaluetype.bunki, frame.data[1]);
|
||||||
|
SetSTS(estsvaluetype.speed, frame.data[2]);
|
||||||
|
SetSTS(estsvaluetype.sensor, frame.data[3]);
|
||||||
|
break;
|
||||||
|
case "CRT"://수동제어
|
||||||
|
sts_dir = frame.data[0];
|
||||||
|
sts_bunki = frame.data[1];
|
||||||
|
sts_speed = frame.data[2];
|
||||||
|
sts_sensor = frame.data[3];
|
||||||
|
SetAGV(esystemflag1.agv_stop, false);
|
||||||
|
SetAGV(esystemflag1.agv_run, true);
|
||||||
|
break;
|
||||||
|
case "ACK": //응답ok
|
||||||
|
RaiseMessage(MessageType.Normal, $">> {frame.cmd} DATA={frame.data}");
|
||||||
|
break;
|
||||||
|
case "SFR": //reset
|
||||||
|
SetAGV(DevAGV.eerror.Emergency, false);
|
||||||
|
SetAGV(DevAGV.eerror.line_out_error, false);
|
||||||
|
SetAGV(DevAGV.eerror.Overcurrent, false);
|
||||||
|
SetAGV(DevAGV.esystemflag1.agv_run, false);
|
||||||
|
SetAGV(DevAGV.esystemflag1.agv_stop, true);
|
||||||
|
break;
|
||||||
|
case "CBT"://충전작업
|
||||||
|
SetAGV(DevAGV.esystemflag1.Battery_charging, true);
|
||||||
|
var id = frame.data.Substring(2, 2);
|
||||||
|
var cmd = frame.data[4];
|
||||||
|
var delaytime = int.Parse(frame.data.Substring(5));
|
||||||
|
if (cmd == 'I') SetAGV(DevAGV.esystemflag1.Battery_charging, true);
|
||||||
|
else SetAGV(esystemflag1.Battery_charging, false);
|
||||||
|
break;
|
||||||
|
case "SSH":
|
||||||
|
case "SSM":
|
||||||
|
case "SSL":
|
||||||
|
case "SSS":
|
||||||
|
case "SHS":
|
||||||
|
case "SLS":
|
||||||
|
case "SPK":
|
||||||
|
case "SPM":
|
||||||
|
case "SPL":
|
||||||
|
case "SPS":
|
||||||
|
case "SIK":
|
||||||
|
case "SIM":
|
||||||
|
case "SIH":
|
||||||
|
case "SIL":
|
||||||
|
case "SIS":
|
||||||
|
case "SDK":
|
||||||
|
case "SDW":
|
||||||
|
case "SDL":
|
||||||
|
case "SDS":
|
||||||
|
case "SRS":
|
||||||
|
case "SCK":
|
||||||
|
case "SSK":
|
||||||
|
case "STT":
|
||||||
|
case "SSI":
|
||||||
|
case "SMD":
|
||||||
|
case "SSC":
|
||||||
|
case "SPN":
|
||||||
|
case "SPH":
|
||||||
|
case "SCH":
|
||||||
|
case "SDH":
|
||||||
|
case "SDM":
|
||||||
|
case "SLB":
|
||||||
|
case "SGS":
|
||||||
|
SendCmd("ACK", frame.cmd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RaiseMessage(MessageType.Normal, $"미처리명령 {frame.cmd} DATA={frame.data}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public void SendCmd(string cmd, string value)
|
||||||
|
{
|
||||||
|
|
||||||
|
var barr = new List<byte>();
|
||||||
|
barr.Add(0x02);
|
||||||
|
barr.AddRange(System.Text.Encoding.Default.GetBytes(cmd));
|
||||||
|
barr.AddRange(System.Text.Encoding.Default.GetBytes(value));
|
||||||
|
barr.Add((byte)'*');
|
||||||
|
barr.Add((byte)'*');
|
||||||
|
barr.Add(0x03);
|
||||||
|
var cmdstr = System.Text.Encoding.Default.GetString(barr.ToArray());
|
||||||
|
RaiseMessage(MessageType.Normal, "Tx:" + barr.ToArray().HexString());
|
||||||
|
WriteData(barr.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendTag(string tagno)
|
||||||
|
{
|
||||||
|
tagno = tagno.PadLeft(6, '0');
|
||||||
|
if (tagno.Length > 6) tagno = tagno.Substring(0, 6);
|
||||||
|
var barr = new List<byte>();
|
||||||
|
barr.Add(0x02);
|
||||||
|
barr.Add((byte)'T');
|
||||||
|
barr.Add((byte)'A');
|
||||||
|
barr.Add((byte)'G');
|
||||||
|
barr.AddRange(System.Text.Encoding.Default.GetBytes(tagno));
|
||||||
|
barr.Add((byte)'*');
|
||||||
|
barr.Add((byte)'*');
|
||||||
|
barr.Add(0x03);
|
||||||
|
var cmdstr = System.Text.Encoding.Default.GetString(barr.ToArray());
|
||||||
|
RaiseMessage(MessageType.Normal, "Tx:" + barr.ToArray().HexString());
|
||||||
|
WriteData(barr.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AutoSendData()
|
||||||
|
{
|
||||||
|
//if (_device.DtrEnable == false) return;
|
||||||
|
//STS258FFFF40000000LSF0000003A
|
||||||
|
//02 53 54 53 32 35 38 46 46 46 46 34 30 30 30 30 30 30 30 4C 53 46 30 30 30 30 30 30 33 41 03
|
||||||
|
var sample = "02 53 54 53 32 35 38 46 46 46 46 34 30 30 30 30 30 30 30 4C 53 46 30 30 30 30 30 30 33 41 03";
|
||||||
|
var barr = sample.Split(' ').ToList().Select(t => Convert.ToByte(t, 16)).ToArray();
|
||||||
|
|
||||||
|
//if (RequestStatusData != null)
|
||||||
|
//{
|
||||||
|
//var p = new RequestStatusDataArgs();
|
||||||
|
//RequestStatusData.Invoke(this, p);
|
||||||
|
|
||||||
|
var voltstr = "255";// ((int)(p.volt * 10f)).ToString().PadRight(3, '0');
|
||||||
|
var bufarr = System.Text.Encoding.Default.GetBytes(voltstr);
|
||||||
|
Array.Copy(bufarr, 0, barr, 4, bufarr.Length);
|
||||||
|
|
||||||
|
bufarr = System.Text.Encoding.Default.GetBytes(system0.ToString("X2").PadLeft(4, '0'));
|
||||||
|
|
||||||
|
Array.Copy(bufarr, 0, barr, 7, bufarr.Length);
|
||||||
|
|
||||||
|
bufarr = System.Text.Encoding.Default.GetBytes(system1.ToString("X2").PadLeft(4, '0'));
|
||||||
|
Array.Copy(bufarr, 0, barr, 11, bufarr.Length);
|
||||||
|
|
||||||
|
bufarr = System.Text.Encoding.Default.GetBytes(error.ToString("X2").PadLeft(4, '0'));
|
||||||
|
Array.Copy(bufarr, 0, barr, 15, bufarr.Length);
|
||||||
|
|
||||||
|
|
||||||
|
barr[19] = (byte)this.sts_speed;
|
||||||
|
barr[20] = (byte)this.sts_bunki;
|
||||||
|
barr[21] = (byte)this.sts_dir;
|
||||||
|
barr[22] = (byte)this.sts_sensor;
|
||||||
|
//bufarr = System.Text.Encoding.Default.GetBytes(p.sensor.ToString().PadLeft(2, '0'));
|
||||||
|
//Array.Copy(bufarr, 0, barr, 22, bufarr.Length);
|
||||||
|
bufarr = System.Text.Encoding.Default.GetBytes(signal.ToString("X2").PadLeft(2, '0'));
|
||||||
|
Array.Copy(bufarr, 0, barr, 23, bufarr.Length);
|
||||||
|
//barr[22] = (byte)'5';
|
||||||
|
|
||||||
|
barr[barr.Length - 3] = (byte)'*';
|
||||||
|
barr[barr.Length - 2] = (byte)'*';
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
var cmdstr = System.Text.Encoding.Default.GetString(barr);
|
||||||
|
RaiseMessage(MessageType.Normal, "Tx:" + barr.ToArray().HexString());
|
||||||
|
WriteData(barr.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
|
||||||
|
{
|
||||||
|
//DD A5 03 00 FF FD 77 0D
|
||||||
|
//remainBuffer = new byte[] { };
|
||||||
|
List<byte> remain = new List<byte>();
|
||||||
|
bool retval = false;
|
||||||
|
foreach (var b in buf)
|
||||||
|
{
|
||||||
|
if (retval)
|
||||||
|
{
|
||||||
|
remain.Add(b);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (b == 0x02) //stx
|
||||||
|
{
|
||||||
|
tempBuffer.Clear();
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (b == 0x03) //etx
|
||||||
|
{
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
retval = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//데이터길이가 만족한 상태
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
var maxlen = 100;
|
||||||
|
if (tempBuffer.Count > maxlen)
|
||||||
|
{
|
||||||
|
RaiseMessage(MessageType.Error, $"buffer over({maxlen})");
|
||||||
|
tempBuffer.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
remainBuffer = remain.ToArray();
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
AGVEmulator/DevXBE.cs
Normal file
93
AGVEmulator/DevXBE.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using ENIG;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
public class DevXBE : AR.Dev.RS232
|
||||||
|
{
|
||||||
|
private EEProtocol proto;
|
||||||
|
public event EventHandler<EEProtocol.DataEventArgs> ProtocReceived;
|
||||||
|
|
||||||
|
public DevXBE()
|
||||||
|
{
|
||||||
|
proto = new EEProtocol();
|
||||||
|
proto.OnDataReceived += Proto_OnDataReceived;
|
||||||
|
proto.OnMessage += Proto_OnMessage;
|
||||||
|
}
|
||||||
|
~DevXBE()
|
||||||
|
{
|
||||||
|
proto.OnDataReceived -= Proto_OnDataReceived;
|
||||||
|
proto.OnMessage -= Proto_OnMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ProcessRecvData(byte[] data)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
|
||||||
|
{
|
||||||
|
//여기서 최초데이터를 파싱한다
|
||||||
|
remainBuffer = null;
|
||||||
|
this.proto.ProcessReceivedData(buf);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Proto_OnDataReceived(object sender, EEProtocol.DataEventArgs e)
|
||||||
|
{
|
||||||
|
var hexstrRaw = e.ReceivedPacket.RawData.HexString();
|
||||||
|
var hexstr = e.ReceivedPacket.Data.HexString();
|
||||||
|
var cmd = e.ReceivedPacket.Command.ToString("X2");
|
||||||
|
var id = e.ReceivedPacket.ID.ToString("X2");
|
||||||
|
|
||||||
|
var dataStr = System.Text.Encoding.Default.GetString(e.ReceivedPacket.Data);
|
||||||
|
RaiseMessage(MessageType.Recv, $"ID:{id},CMD:{cmd},DATA:{hexstr}");
|
||||||
|
ProtocReceived?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
|
||||||
|
{
|
||||||
|
RaiseMessage(e.IsError, e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 목적지 태그번호 전송
|
||||||
|
/// </summary>
|
||||||
|
public void SendGotoTag(byte id, uint tag)
|
||||||
|
{
|
||||||
|
var idSTR = id.ToString("X2");
|
||||||
|
var tagSTR = tag.ToString("0000");
|
||||||
|
var dataStr = $"{idSTR}{tagSTR}";
|
||||||
|
Send(ENIGProtocol.AGVCommandHE.Goto, dataStr);
|
||||||
|
}
|
||||||
|
public void SendCurrentPos(byte id, uint tag)
|
||||||
|
{
|
||||||
|
var idSTR = id.ToString("X2");
|
||||||
|
var tagSTR = tag.ToString("0000");
|
||||||
|
var dataStr = $"{idSTR}{tagSTR}";
|
||||||
|
Send(ENIGProtocol.AGVCommandHE.SetCurrent, dataStr);
|
||||||
|
}
|
||||||
|
private void Send(ENIGProtocol.AGVCommandHE Command, string datastr)
|
||||||
|
{
|
||||||
|
byte id = 0;
|
||||||
|
byte cmd = (byte)Command; //move to target
|
||||||
|
byte[] data = null;
|
||||||
|
if (datastr != null && string.IsNullOrEmpty(datastr) == false)
|
||||||
|
data = System.Text.Encoding.Default.GetBytes(datastr);
|
||||||
|
var packet = proto.CreatePacket(id, cmd, data);
|
||||||
|
if (WriteData(packet, false))
|
||||||
|
{
|
||||||
|
var hexstr = System.Text.Encoding.Default.GetString(data);
|
||||||
|
RaiseMessage(MessageType.Send, $"ID:{id},CMD:{cmd},DATA:{hexstr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
34
AGVEmulator/Program.cs
Normal file
34
AGVEmulator/Program.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 해당 애플리케이션의 주 진입점입니다.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new fMain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class methodext
|
||||||
|
{
|
||||||
|
public static string HexString(this byte[] buf)
|
||||||
|
{
|
||||||
|
var sb = new System.Text.StringBuilder();
|
||||||
|
foreach (var b in buf)
|
||||||
|
{
|
||||||
|
sb.Append(" " + b.ToString("X2"));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
AGVEmulator/Properties/AssemblyInfo.cs
Normal file
36
AGVEmulator/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||||
|
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||||
|
// 이러한 특성 값을 변경하세요.
|
||||||
|
[assembly: AssemblyTitle("AGVEmulator")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("AGVEmulator")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||||
|
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
|
||||||
|
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||||
|
[assembly: Guid("9312ab43-72f6-FF65-a266-e767215fa7f5")]
|
||||||
|
|
||||||
|
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||||
|
//
|
||||||
|
// 주 버전
|
||||||
|
// 부 버전
|
||||||
|
// 빌드 번호
|
||||||
|
// 수정 버전
|
||||||
|
//
|
||||||
|
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
||||||
|
// 기본값으로 할 수 있습니다.
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.1")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.1")]
|
||||||
63
AGVEmulator/Properties/Resources.Designer.cs
generated
Normal file
63
AGVEmulator/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||||
|
// 런타임 버전:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||||
|
// 이러한 변경 내용이 손실됩니다.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace AGVEmulator.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
|
||||||
|
/// </summary>
|
||||||
|
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
|
||||||
|
// 클래스에서 자동으로 생성되었습니다.
|
||||||
|
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을
|
||||||
|
// 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.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("AGVEmulator.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
AGVEmulator/Properties/Resources.resx
Normal file
117
AGVEmulator/Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?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.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: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" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</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" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
26
AGVEmulator/Properties/Settings.Designer.cs
generated
Normal file
26
AGVEmulator/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||||
|
// 런타임 버전:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||||
|
// 이러한 변경 내용이 손실됩니다.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace AGVEmulator.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
AGVEmulator/Properties/Settings.settings
Normal file
7
AGVEmulator/Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
485
AGVEmulator/RS232.cs
Normal file
485
AGVEmulator/RS232.cs
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace AR.Dev
|
||||||
|
{
|
||||||
|
public abstract partial class RS232 : IDisposable
|
||||||
|
{
|
||||||
|
protected System.IO.Ports.SerialPort _device;
|
||||||
|
protected ManualResetEvent _mre;
|
||||||
|
protected byte[] LastReceiveBuffer;
|
||||||
|
|
||||||
|
protected List<byte> tempBuffer = new List<byte>();
|
||||||
|
protected Boolean findSTX = false;
|
||||||
|
|
||||||
|
|
||||||
|
public int WriteError = 0;
|
||||||
|
public string WriteErrorMessage = string.Empty;
|
||||||
|
public int WaitTimeout { get; set; } = 1000;
|
||||||
|
public int MinRecvLength { get; set; } = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// 포트이름
|
||||||
|
/// </summary>
|
||||||
|
[Description("시리얼 포트 이름")]
|
||||||
|
[Category("설정"), DisplayName("Port Name")]
|
||||||
|
public string PortName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return string.Empty;
|
||||||
|
else return _device.PortName;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsOpen)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
|
||||||
|
}
|
||||||
|
else if (String.IsNullOrEmpty(value) == false)
|
||||||
|
_device.PortName = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BaudRate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return 0;
|
||||||
|
else return _device.BaudRate;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsOpen)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
|
||||||
|
}
|
||||||
|
else if (value != 0)
|
||||||
|
_device.BaudRate = value;
|
||||||
|
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string errorMessage { get; set; }
|
||||||
|
public DateTime LastConnTime { get; set; }
|
||||||
|
public DateTime LastConnTryTime { get; set; }
|
||||||
|
public DateTime lastSendTime;
|
||||||
|
/// <summary>
|
||||||
|
/// 메세지 수신시 사용하는 내부버퍼
|
||||||
|
/// </summary>
|
||||||
|
protected List<byte> _buffer = new List<byte>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 최종 전송 메세지
|
||||||
|
/// </summary>
|
||||||
|
public byte[] lastSendBuffer;
|
||||||
|
public byte[] LastRecvData;
|
||||||
|
public string LastRecvString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LastRecvData == null) return String.Empty;
|
||||||
|
else return System.Text.Encoding.Default.GetString(LastRecvData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 마지막으로 데이터를 받은 시간
|
||||||
|
/// </summary>
|
||||||
|
public DateTime lastRecvTime;
|
||||||
|
|
||||||
|
public RS232()
|
||||||
|
{
|
||||||
|
_device = new System.IO.Ports.SerialPort();
|
||||||
|
this.BaudRate = 9600;
|
||||||
|
|
||||||
|
_device.DataReceived += barcode_DataReceived;
|
||||||
|
_device.ErrorReceived += this.barcode_ErrorReceived;
|
||||||
|
_device.WriteTimeout = 1000;
|
||||||
|
_device.ReadTimeout = 1000;
|
||||||
|
_device.DtrEnable = false;
|
||||||
|
_device.RtsEnable = false;
|
||||||
|
_device.ReadBufferSize = 8192;
|
||||||
|
_device.WriteBufferSize = 8192;
|
||||||
|
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||||
|
LastConnTime = DateTime.Parse("1982-11-23");
|
||||||
|
LastConnTryTime = DateTime.Parse("1982-11-23");
|
||||||
|
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||||
|
this._mre = new ManualResetEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RS232()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Flag: Has Dispose already been called?
|
||||||
|
bool disposed = false;
|
||||||
|
|
||||||
|
// Public implementation of Dispose pattern callable by consumers.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected implementation of Dispose pattern.
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Free any other managed objects here.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
_device.DataReceived -= barcode_DataReceived;
|
||||||
|
_device.ErrorReceived -= this.barcode_ErrorReceived;
|
||||||
|
|
||||||
|
// Free any unmanaged objects here.
|
||||||
|
//
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean Open()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_device.Open();
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(MessageType.Normal, $"port open:{_device.IsOpen}"));
|
||||||
|
return IsOpen;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = ex.Message;
|
||||||
|
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string GetHexString(Byte[] input)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
|
foreach (byte b in input)
|
||||||
|
sb.Append(" " + b.ToString("X2"));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 포트가 열려있는지 확인
|
||||||
|
/// </summary>
|
||||||
|
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
|
||||||
|
[Category("정보"), DisplayName("Port Open")]
|
||||||
|
public Boolean IsOpen
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return false;
|
||||||
|
return _device.IsOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Close(Boolean PortClose = true)
|
||||||
|
{
|
||||||
|
if (_device != null && _device.IsOpen)
|
||||||
|
{
|
||||||
|
_device.DiscardInBuffer();
|
||||||
|
_device.DiscardOutBuffer();
|
||||||
|
if (PortClose) _device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean RaiseRecvData()
|
||||||
|
{
|
||||||
|
return RaiseRecvData(LastReceiveBuffer.ToArray());
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 수신받은 메세지를 발생 시킵니다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual Boolean RaiseRecvData(byte[] Data)
|
||||||
|
{
|
||||||
|
//181206 - 최종수신 메세지 기록
|
||||||
|
lastRecvTime = DateTime.Now;
|
||||||
|
LastRecvData = Data;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||||
|
if (ProcessRecvData(Data) == false)
|
||||||
|
{
|
||||||
|
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(this.errorMessage, true)); //errormessage
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.errorMessage = ex.Message;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 수신받은 자료를 처리한다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract bool ProcessRecvData(byte[] data);
|
||||||
|
|
||||||
|
#region "Internal Events"
|
||||||
|
|
||||||
|
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int ReadCount = _device.BytesToRead;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[ReadCount];
|
||||||
|
_device.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
System.Text.StringBuilder LogMsg = new StringBuilder();
|
||||||
|
|
||||||
|
byte[] remainBuffer;
|
||||||
|
Repeat:
|
||||||
|
if (CustomParser(buffer, out remainBuffer))
|
||||||
|
{
|
||||||
|
//parser ok
|
||||||
|
LastReceiveBuffer = tempBuffer.ToArray();
|
||||||
|
RaiseRecvData();
|
||||||
|
tempBuffer.Clear();
|
||||||
|
if (remainBuffer != null && remainBuffer.Length > 0)
|
||||||
|
{
|
||||||
|
//버퍼를 변경해서 다시 전송을 해준다.
|
||||||
|
buffer = new byte[remainBuffer.Length];
|
||||||
|
Array.Copy(remainBuffer, buffer, buffer.Length);
|
||||||
|
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (IsOpen)
|
||||||
|
{
|
||||||
|
//_device.DiscardInBuffer();
|
||||||
|
//_device.DiscardOutBuffer();
|
||||||
|
}
|
||||||
|
errorMessage = ex.Message;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "External Events"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 오류 및 기타 일반 메세지
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<MessageEventArgs> Message;
|
||||||
|
|
||||||
|
public void RaiseMessage(bool iserr, string message)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(message, iserr));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "Event Args"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 데이터를 수신할떄 사용함(RAW 포함)
|
||||||
|
/// </summary>
|
||||||
|
public class ReceiveDataEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private byte[] _buffer = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 바이트배열의 버퍼값
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Value { get { return _buffer; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
|
||||||
|
/// </summary>
|
||||||
|
public string StrValue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
//return string.Empty;
|
||||||
|
|
||||||
|
if (_buffer == null || _buffer.Length < 1) return string.Empty;
|
||||||
|
else return System.Text.Encoding.Default.GetString(_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public ReceiveDataEventArgs(byte[] buffer)
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메세지를 강제 발생
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mt"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
protected virtual void RaiseMessage(MessageType mt, string message)
|
||||||
|
{
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
|
||||||
|
}
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Error,
|
||||||
|
Send,
|
||||||
|
Recv,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MessageEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MessageType MsgType { get; set; }
|
||||||
|
private string _message = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recv,Send,Normal,Error 모두 지원
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get { return _message; } }
|
||||||
|
|
||||||
|
private byte[] _data = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recv,Send에서만 값이 존재 합니다
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Data { get { return _data; } }
|
||||||
|
public MessageEventArgs(string Message, bool isError = false)
|
||||||
|
{
|
||||||
|
if (isError) MsgType = MessageType.Error;
|
||||||
|
else MsgType = MessageType.Normal;
|
||||||
|
_message = Message;
|
||||||
|
}
|
||||||
|
public MessageEventArgs(MessageType msgtype, string Message)
|
||||||
|
{
|
||||||
|
MsgType = msgtype;
|
||||||
|
_message = Message;
|
||||||
|
_data = System.Text.Encoding.Default.GetBytes(Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageEventArgs(byte[] buffer, bool isRecv = true)
|
||||||
|
{
|
||||||
|
if (isRecv) MsgType = MessageType.Recv;
|
||||||
|
else MsgType = MessageType.Send;
|
||||||
|
_data = new byte[buffer.Length];
|
||||||
|
Array.Copy(buffer, _data, Data.Length);
|
||||||
|
_message = System.Text.Encoding.Default.GetString(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
public virtual void AutoSendData()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 데이터수신시간이 설정값보다 x 2.5를 초과하면 false 가 반환됨
|
||||||
|
/// </summary>
|
||||||
|
public Boolean IsValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsOpen == false) return false;
|
||||||
|
if (lastRecvTime.Year == 1982) return false;
|
||||||
|
var ts = DateTime.Now - lastRecvTime;
|
||||||
|
if (ts.TotalSeconds > 5) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool WriteData(string cmd)
|
||||||
|
{
|
||||||
|
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
|
||||||
|
/// </summary>
|
||||||
|
public Boolean WriteData(byte[] data, bool useLog = true)
|
||||||
|
{
|
||||||
|
Boolean bRet = false;
|
||||||
|
|
||||||
|
//171205 : 타임아웃시간추가
|
||||||
|
if (!_mre.WaitOne(WaitTimeout))
|
||||||
|
{
|
||||||
|
errorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(errorMessage, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mre.Reset();
|
||||||
|
|
||||||
|
//Array.Resize(ref data, data.Length + 2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lastSendTime = DateTime.Now;
|
||||||
|
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
|
||||||
|
else Array.Resize(ref lastSendBuffer, data.Length);
|
||||||
|
Array.Copy(data, lastSendBuffer, data.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
_device.Write(data, i, 1);
|
||||||
|
|
||||||
|
//_device.Write(data, 0, data.Length);
|
||||||
|
|
||||||
|
//171113
|
||||||
|
if (useLog) this.Message?.Invoke(this, new MessageEventArgs(data, false));
|
||||||
|
|
||||||
|
bRet = true;
|
||||||
|
WriteError = 0;
|
||||||
|
WriteErrorMessage = string.Empty;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// this.isinit = false;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
bRet = false;
|
||||||
|
WriteError += 1; //연속쓰기오류횟수
|
||||||
|
WriteErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mre.Set();
|
||||||
|
}
|
||||||
|
return bRet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
AGVEmulator/RunCode/_AGV.cs
Normal file
214
AGVEmulator/RunCode/_AGV.cs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using static AGVEmulator.DevAGV;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
private void Agv_Command(object sender, commandargs e)
|
||||||
|
{
|
||||||
|
switch (e.Command.ToLower())
|
||||||
|
{
|
||||||
|
case "stopmark":
|
||||||
|
agvViewer1.StopbyMark = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void Agv_StsValueChanged(object sender, DevAGV.StsValueChangedArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"STS [{e.vtype}] VAL={e.Value}");
|
||||||
|
if (this.InvokeRequired)
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new EventHandler<StsValueChangedArgs>(Agv_StsValueChanged), sender, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (e.vtype)
|
||||||
|
{
|
||||||
|
case DevAGV.estsvaluetype.direction:
|
||||||
|
foreach (RadioButton c in this.groupBox5.Controls)
|
||||||
|
{
|
||||||
|
if (c.Text[0].Equals(e.Value))
|
||||||
|
{
|
||||||
|
c.Checked = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c.Checked = false;
|
||||||
|
}
|
||||||
|
c.Refresh();
|
||||||
|
}
|
||||||
|
groupBox5.Refresh();
|
||||||
|
break;
|
||||||
|
case DevAGV.estsvaluetype.bunki:
|
||||||
|
foreach (RadioButton c in this.groupBox2.Controls)
|
||||||
|
{
|
||||||
|
if (c.Text[0].Equals(e.Value))
|
||||||
|
{
|
||||||
|
c.Checked = true;
|
||||||
|
c.Refresh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DevAGV.estsvaluetype.speed:
|
||||||
|
foreach (RadioButton c in this.groupBox4.Controls)
|
||||||
|
{
|
||||||
|
if (c.Text[0].Equals(e.Value))
|
||||||
|
{
|
||||||
|
c.Checked = true;
|
||||||
|
c.Refresh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DevAGV.estsvaluetype.sensor:
|
||||||
|
foreach (RadioButton c in this.groupBox8.Controls)
|
||||||
|
{
|
||||||
|
if (c.Text.Equals(e.Value))
|
||||||
|
{
|
||||||
|
c.Checked = true;
|
||||||
|
c.Refresh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Agv_ValueChanged(object sender, DevAGV.ValueChangedArgs e)
|
||||||
|
{
|
||||||
|
if (this.InvokeRequired)
|
||||||
|
{
|
||||||
|
this.Invoke(new EventHandler<ValueChangedArgs>(Agv_ValueChanged), sender, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//내부값이 바뀌었다면 컨트롤을 변경해준다.
|
||||||
|
switch (e.vtype)
|
||||||
|
{
|
||||||
|
|
||||||
|
case DevAGV.evaluetype.system0:
|
||||||
|
foreach (CheckBox c in panel6.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
|
if (idx == e.Idx)
|
||||||
|
{
|
||||||
|
c.Checked = e.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DevAGV.evaluetype.system1:
|
||||||
|
|
||||||
|
var v = (DevAGV.esystemflag1)e.Idx;
|
||||||
|
if (e.Value)
|
||||||
|
{
|
||||||
|
if (v == esystemflag1.agv_run)
|
||||||
|
{
|
||||||
|
agvViewer1.wat.Restart();
|
||||||
|
}
|
||||||
|
if (v == esystemflag1.agv_stop)
|
||||||
|
{
|
||||||
|
agvViewer1.wat.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (CheckBox c in panel7.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
|
if (idx == e.Idx)
|
||||||
|
{
|
||||||
|
c.Checked = e.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DevAGV.evaluetype.error:
|
||||||
|
foreach (CheckBox c in panel9.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
|
if (idx == e.Idx)
|
||||||
|
{
|
||||||
|
c.Checked = e.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DevAGV.evaluetype.signal:
|
||||||
|
foreach (CheckBox c in panel8.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
|
if (idx == e.Idx)
|
||||||
|
{
|
||||||
|
c.Checked = e.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Agv_RequestStatusData(object sender, DevAGV.RequestStatusDataArgs e)
|
||||||
|
{
|
||||||
|
//UInt16 system0 = 0xFFFF;
|
||||||
|
//UInt16 system1 = 0xFFFF;
|
||||||
|
//UInt16 error = 0xFFFF;
|
||||||
|
//byte signal = 0xFF;
|
||||||
|
|
||||||
|
aaplycheckboxbit(ref AGV.system0, panel6);
|
||||||
|
aaplycheckboxbit(ref AGV.system1, panel7);
|
||||||
|
aaplycheckboxbit(ref AGV.error, panel9);
|
||||||
|
aaplycheckboxbit(ref AGV.signal, panel8);
|
||||||
|
|
||||||
|
if (this.agvViewer1.StopbyMark) AGV.sts_speed = 'S';
|
||||||
|
else AGV.sts_speed = GetGroupItemCheckbox(groupBox4);
|
||||||
|
AGV.sts_bunki = GetGroupItemCheckbox(groupBox2);
|
||||||
|
AGV.sts_dir = GetGroupItemCheckbox(groupBox5);
|
||||||
|
AGV.sts_sensor = GetGroupItemCheckbox(groupBox8);
|
||||||
|
|
||||||
|
//this.Invoke(new Action(() =>
|
||||||
|
//{
|
||||||
|
// e.system0 = system0;
|
||||||
|
// e.system1 = system1;
|
||||||
|
// e.error = error;
|
||||||
|
// e.signal = signal;
|
||||||
|
// e.speed = GetGroupItemCheckbox(groupBox4);
|
||||||
|
// e.bunki = GetGroupItemCheckbox(groupBox2);
|
||||||
|
// e.direction = GetGroupItemCheckbox(groupBox5);
|
||||||
|
// e.sensor = GetGroupItemCheckbox(groupBox8);
|
||||||
|
|
||||||
|
// e.volt = 23.4f;
|
||||||
|
//}));
|
||||||
|
}
|
||||||
|
private void AGV_Message(object sender, AR.Dev.RS232.MessageEventArgs e)
|
||||||
|
{
|
||||||
|
var dev = sender as AR.Dev.RS232;
|
||||||
|
if (dev is DevBMS)
|
||||||
|
{
|
||||||
|
logBMS.Add(e.Message);
|
||||||
|
}
|
||||||
|
else if (dev is DevAGV)
|
||||||
|
{
|
||||||
|
logAGV.Add(e.Message);
|
||||||
|
}
|
||||||
|
else if (dev is DevXBE)
|
||||||
|
{
|
||||||
|
if (e.MsgType == AR.Dev.RS232.MessageType.Send)
|
||||||
|
logCAL.Add(">> " + e.Data.HexString());
|
||||||
|
else if (e.MsgType == AR.Dev.RS232.MessageType.Recv)
|
||||||
|
logCAL.Add("<< " + e.Data.HexString());
|
||||||
|
else logCAL.Add(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
AGVEmulator/RunCode/_BMS.cs
Normal file
63
AGVEmulator/RunCode/_BMS.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
private void BMS_Message(object sender, AR.Dev.RS232.MessageEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
logBMS.Add(e.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BMS_RequestVoltageData(object sender, DevBMS.RequestVoltageDataArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
//cell전압값추가
|
||||||
|
for (int i = 0; i < cellvolt.Length; i++)
|
||||||
|
{
|
||||||
|
this.cellvolt[i] = (UInt16)rnd.Next(3300, 3350);
|
||||||
|
}
|
||||||
|
Array.Copy(this.cellvolt, 0, e.cellVolt, 0, 8);
|
||||||
|
|
||||||
|
this.btc1.Invoke(new Action(() =>
|
||||||
|
{
|
||||||
|
var idx = 0;
|
||||||
|
btc1.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc2.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc3.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc4.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc5.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc6.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc7.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
btc8.Text = (this.cellvolt[idx++] / 1000f).ToString();
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
private void Bms_RequestBatteryData(object sender, DevBMS.RequestBatteryDataArgs e)
|
||||||
|
{
|
||||||
|
if (checkBox1.Checked)
|
||||||
|
this.trackBar1.Invoke(new Action(() =>
|
||||||
|
{
|
||||||
|
this.trackBar1.Value -= 1;
|
||||||
|
trackBar1_Scroll(null, null);
|
||||||
|
}));
|
||||||
|
|
||||||
|
e.CurA = (int)BMS_CurA;
|
||||||
|
e.MaxA = (int)BMS_MaxA;
|
||||||
|
e.Remain = BMS_Remain;
|
||||||
|
e.Volt = BMS_Volt;
|
||||||
|
e.Temp1 = this.Temp1;
|
||||||
|
e.Temp2 = this.Temp2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
40
AGVEmulator/RunCode/_XBEE.cs
Normal file
40
AGVEmulator/RunCode/_XBEE.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using ENIG;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
|
||||||
|
private void CAL_Message(object sender, AR.Dev.RS232.MessageEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (e.MsgType == AR.Dev.RS232.MessageType.Send)
|
||||||
|
logCAL.Add(">> " + System.Text.Encoding.Default.GetString(e.Data));
|
||||||
|
else if (e.MsgType == AR.Dev.RS232.MessageType.Recv)
|
||||||
|
logCAL.Add("<< " + System.Text.Encoding.Default.GetString( e.Data));
|
||||||
|
else logCAL.Add(e.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
private void CAL_ProtocReceived(object sender, ENIG.EEProtocol.DataEventArgs e)
|
||||||
|
{
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
var dev = (DeviceType)e.ReceivedPacket.ID;
|
||||||
|
if (dev == DeviceType.AGV1 || dev == DeviceType.AGV2)
|
||||||
|
{
|
||||||
|
//agv에서 들어오는 데이터
|
||||||
|
var cmd = e.ReceivedPacket.Command;
|
||||||
|
if(cmd == 3)
|
||||||
|
{
|
||||||
|
//status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AGVEmulator/Sample/packetFC.bin
Normal file
BIN
AGVEmulator/Sample/packetFC.bin
Normal file
Binary file not shown.
BIN
AGVEmulator/Sample/packetFD.bin
Normal file
BIN
AGVEmulator/Sample/packetFD.bin
Normal file
Binary file not shown.
37
AGVEmulator/UC/AgvViewer.Designer.cs
generated
Normal file
37
AGVEmulator/UC/AgvViewer.Designer.cs
generated
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
namespace AGVEmulator.UC
|
||||||
|
{
|
||||||
|
partial class AgvViewer
|
||||||
|
{
|
||||||
|
/// <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
|
||||||
|
}
|
||||||
|
}
|
||||||
272
AGVEmulator/UC/AgvViewer.cs
Normal file
272
AGVEmulator/UC/AgvViewer.cs
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
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 AGVEmulator.UC
|
||||||
|
{
|
||||||
|
public partial class AgvViewer : Control
|
||||||
|
{
|
||||||
|
|
||||||
|
public AgvViewer()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TagArgs : EventArgs
|
||||||
|
{
|
||||||
|
public string Data { get; set; }
|
||||||
|
public bool Active { get; set; }
|
||||||
|
public TagArgs(string tag,bool act)
|
||||||
|
{
|
||||||
|
this.Data = tag;
|
||||||
|
this.Active = act;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public event EventHandler<TagArgs> TagTouched;
|
||||||
|
public event EventHandler<TagArgs> MarkTouched;
|
||||||
|
public event EventHandler<TagArgs> Command;
|
||||||
|
|
||||||
|
public bool StopbyMark { get; set; }
|
||||||
|
public float mpos = 100;
|
||||||
|
public float posmax = 1200;
|
||||||
|
public float posmin = 0;
|
||||||
|
public float mspd = 10;
|
||||||
|
public System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch();
|
||||||
|
public Font FontTag { get; set; }
|
||||||
|
public Font FontMrk { get; set; }
|
||||||
|
public int dir = 1;
|
||||||
|
|
||||||
|
public class ptdata
|
||||||
|
{
|
||||||
|
public float pos { get; set; } = 0f;
|
||||||
|
public string data { get; set; } = string.Empty;
|
||||||
|
public Boolean active { get; set; } = false;
|
||||||
|
}
|
||||||
|
public ptdata[] listMRK { get; set; }
|
||||||
|
public ptdata[] listTAG { get; set; }
|
||||||
|
|
||||||
|
public string lasttag { get; set; } = string.Empty;
|
||||||
|
public string lasttagdir { get; set; } = string.Empty;
|
||||||
|
public string lastmark { get; set; } = string.Empty;
|
||||||
|
public string lastmarkdir { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs pe)
|
||||||
|
{
|
||||||
|
pe.Graphics.Clear(this.BackColor);
|
||||||
|
var r = new Rectangle(DisplayRectangle.Left + Padding.Left,
|
||||||
|
DisplayRectangle.Top + Padding.Top,
|
||||||
|
DisplayRectangle.Width - Padding.Right - Padding.Left - 1,
|
||||||
|
DisplayRectangle.Height - Padding.Top - Padding.Bottom - 1);
|
||||||
|
|
||||||
|
|
||||||
|
// pe.Graphics.FillRectangle(new SolidBrush(this.BackColor), DisplayRectangle);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.Black, r);
|
||||||
|
|
||||||
|
|
||||||
|
var ptwidth = 25;
|
||||||
|
var ptheight = 35;
|
||||||
|
if (listMRK != null && listMRK.Any() && FontMrk != null)
|
||||||
|
{
|
||||||
|
foreach (var item in listMRK)
|
||||||
|
{
|
||||||
|
var x = r.Left + ((item.pos * 1f) / posmax) * r.Width;
|
||||||
|
var rr = new RectangleF(x - ptwidth, r.Top + r.Height / 2f - ptheight / 2f, ptwidth * 2, ptheight);
|
||||||
|
pe.Graphics.DrawLine(Pens.Gray, x, r.Top, x, r.Bottom);
|
||||||
|
pe.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.Gold)), rr);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.DimGray, rr.Left, rr.Top, rr.Width, rr.Height);
|
||||||
|
pe.Graphics.DrawString(item.data, FontMrk, Brushes.Gray, rr, new StringFormat
|
||||||
|
{
|
||||||
|
Alignment = StringAlignment.Center,
|
||||||
|
LineAlignment = StringAlignment.Center,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptwidth = 15;
|
||||||
|
if (listTAG != null && listTAG.Any() && FontTag != null)
|
||||||
|
{
|
||||||
|
var lst = listTAG.Where(t => t.data.EndsWith("1"));
|
||||||
|
foreach (var item in lst)
|
||||||
|
{
|
||||||
|
var x = r.Left + ((item.pos * 1f) / posmax) * r.Width;
|
||||||
|
var rr = new RectangleF(x - ptwidth, r.Top + 5, ptwidth * 2, 15);
|
||||||
|
pe.Graphics.DrawLine(Pens.Orange, x, r.Top, x, rr.Top);
|
||||||
|
pe.Graphics.FillRectangle(Brushes.Orange, rr);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.DimGray, rr.Left, rr.Top, rr.Width, rr.Height);
|
||||||
|
pe.Graphics.DrawString(item.data, FontTag, Brushes.Black, rr, new StringFormat
|
||||||
|
{
|
||||||
|
Alignment = StringAlignment.Center,
|
||||||
|
LineAlignment = StringAlignment.Center,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
lst = listTAG.Where(t => t.data.EndsWith("0"));
|
||||||
|
foreach (var item in lst)
|
||||||
|
{
|
||||||
|
var x = r.Left + ((item.pos * 1f) / posmax) * r.Width;
|
||||||
|
var rr = new RectangleF(x - ptwidth, r.Bottom - 20, ptwidth * 2, 15);
|
||||||
|
pe.Graphics.DrawLine(Pens.Orange, x, rr.Bottom, x, r.Bottom);
|
||||||
|
pe.Graphics.FillRectangle(Brushes.Orange, rr);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.DimGray, rr.Left, rr.Top, rr.Width, rr.Height);
|
||||||
|
pe.Graphics.DrawString(item.data, FontTag, Brushes.Black, rr, new StringFormat
|
||||||
|
{
|
||||||
|
Alignment = StringAlignment.Center,
|
||||||
|
LineAlignment = StringAlignment.Center,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var posX = r.Left + (mpos / posmax) * r.Width;
|
||||||
|
var posY = r.Top + r.Height / 2f;
|
||||||
|
var boxw = r.Width * 0.030f;
|
||||||
|
var boxh = r.Height * 0.15f;
|
||||||
|
var box = new RectangleF(posX - boxw, posY - boxh, boxw * 2, boxh * 2);
|
||||||
|
var box2 = new RectangleF(box.Left - 5, box.Top + 3, 10, box.Height - 6);
|
||||||
|
for (int i = 0; i < posmax; i += 100)
|
||||||
|
{
|
||||||
|
var x = r.Left + ((i * 1f) / posmax) * r.Width;
|
||||||
|
pe.Graphics.DrawLine(Pens.Black, x, r.Bottom - 3, x, r.Bottom);
|
||||||
|
if (i > 0)
|
||||||
|
pe.Graphics.DrawString($"{i / 10f}m", this.Font, Brushes.Black, x - 12, r.Bottom - 15);
|
||||||
|
}
|
||||||
|
pe.Graphics.FillRectangle(Brushes.LightSkyBlue, box);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.Black, box.Left, box.Top, box.Width, box.Height);
|
||||||
|
pe.Graphics.DrawLine(new Pen(Color.Black, 4), posX, box.Top - 5, posX, box.Bottom + 5);
|
||||||
|
|
||||||
|
pe.Graphics.FillRectangle(Brushes.Gold, box2);
|
||||||
|
pe.Graphics.DrawRectangle(Pens.Black, box2.Left, box2.Top, box2.Width, box2.Height);
|
||||||
|
//pe.Graphics.DrawString((mpos / 10f).ToString("N1") + "m", this.Font, Brushes.Black, box, new StringFormat
|
||||||
|
//{
|
||||||
|
// Alignment = StringAlignment.Center,
|
||||||
|
// LineAlignment = StringAlignment.Center,
|
||||||
|
//});
|
||||||
|
|
||||||
|
if (StopbyMark)
|
||||||
|
pe.Graphics.DrawString("!MRK-STP!", this.Font, Brushes.Blue, r.Left+2, r.Top+2);
|
||||||
|
|
||||||
|
if (wat.IsRunning)
|
||||||
|
{
|
||||||
|
var newpos = mspd * (wat.ElapsedMilliseconds / 1000f);
|
||||||
|
if (dir < 0) //forward
|
||||||
|
{
|
||||||
|
if (mpos - newpos < 0)
|
||||||
|
mpos = posmax;
|
||||||
|
else mpos -= newpos;
|
||||||
|
|
||||||
|
//내위치주변에 마커가 있는지 본다
|
||||||
|
var mlist = listMRK.Where(t => t.pos <= mpos && (mpos - t.pos) < 10);
|
||||||
|
var mrk = mlist.FirstOrDefault();
|
||||||
|
if (mrk != null)
|
||||||
|
{
|
||||||
|
//대상마커가있다
|
||||||
|
if (lastmark.Equals(mrk.data) == false || lastmarkdir.Equals("F") == false)
|
||||||
|
{
|
||||||
|
lastmark = mrk.data;
|
||||||
|
lastmarkdir = "F";
|
||||||
|
mrk.active = true;
|
||||||
|
MarkTouched?.Invoke(this, new TagArgs(mrk.data,true));
|
||||||
|
if(StopbyMark)
|
||||||
|
{
|
||||||
|
Command?.Invoke(this, new TagArgs("stop",true));
|
||||||
|
StopbyMark = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(lastmark) == false)
|
||||||
|
{
|
||||||
|
foreach(var item in listMRK.Where(t=>t.active))
|
||||||
|
{
|
||||||
|
item.active = false;
|
||||||
|
MarkTouched?.Invoke(this, new TagArgs(item.data,false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//주변태그확인
|
||||||
|
var tlist = listTAG.Where(t => t.pos <= mpos && (mpos - t.pos) < 10);
|
||||||
|
var tag = tlist.FirstOrDefault();
|
||||||
|
if (tag != null)
|
||||||
|
{
|
||||||
|
//대상마커가있다
|
||||||
|
if (lasttag.Equals(tag.data) == false || lasttagdir.Equals("F") == false)
|
||||||
|
{
|
||||||
|
lasttag = tag.data;
|
||||||
|
lasttagdir = "F";
|
||||||
|
TagTouched?.Invoke(this, new TagArgs(tag.data, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //backward
|
||||||
|
{
|
||||||
|
if (mpos + newpos > posmax)
|
||||||
|
mpos = 0;
|
||||||
|
else mpos += newpos;
|
||||||
|
|
||||||
|
//내위치주변에 마커가 있는지 본다
|
||||||
|
var mlist = listMRK.Where(t => t.pos >= mpos && (t.pos - mpos) < 10);
|
||||||
|
var mrk = mlist.FirstOrDefault();
|
||||||
|
if (mrk != null)
|
||||||
|
{
|
||||||
|
//대상마커가있다
|
||||||
|
if (lastmark.Equals(mrk.data) == false || lastmarkdir.Equals("B") == false)
|
||||||
|
{
|
||||||
|
lastmark = mrk.data;
|
||||||
|
lastmarkdir = "B";
|
||||||
|
mrk.active = true;
|
||||||
|
MarkTouched?.Invoke(this, new TagArgs(mrk.data, true));
|
||||||
|
if (StopbyMark)
|
||||||
|
{
|
||||||
|
Command?.Invoke(this, new TagArgs("stop", true));
|
||||||
|
StopbyMark = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(lastmark) == false)
|
||||||
|
{
|
||||||
|
foreach (var item in listMRK.Where(t => t.active))
|
||||||
|
{
|
||||||
|
item.active = false;
|
||||||
|
MarkTouched?.Invoke(this, new TagArgs(item.data, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//주변태그확인
|
||||||
|
var tlist = listTAG.Where(t => t.pos >= mpos && (t.pos - mpos) < 10);
|
||||||
|
var tag = tlist.FirstOrDefault();
|
||||||
|
if (tag != null)
|
||||||
|
{
|
||||||
|
//대상마커가있다
|
||||||
|
if (lasttag.Equals(tag.data) == false || lasttagdir.Equals("B") == false)
|
||||||
|
{
|
||||||
|
lasttag = tag.data;
|
||||||
|
lasttagdir = "B";
|
||||||
|
TagTouched?.Invoke(this, new TagArgs(tag.data, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wat.Restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
AGVEmulator/UC/SerialConn.Designer.cs
generated
Normal file
162
AGVEmulator/UC/SerialConn.Designer.cs
generated
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
partial class SerialConn
|
||||||
|
{
|
||||||
|
/// <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()
|
||||||
|
{
|
||||||
|
this.cmbBaud = new System.Windows.Forms.ComboBox();
|
||||||
|
this.cmbPOrt = new System.Windows.Forms.ComboBox();
|
||||||
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.lbrx = new System.Windows.Forms.Label();
|
||||||
|
this.lbtx = new System.Windows.Forms.Label();
|
||||||
|
this.button1 = new System.Windows.Forms.Button();
|
||||||
|
this.panel1.SuspendLayout();
|
||||||
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// cmbBaud
|
||||||
|
//
|
||||||
|
this.cmbBaud.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
this.cmbBaud.Font = new System.Drawing.Font("굴림", 15F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.cmbBaud.FormattingEnabled = true;
|
||||||
|
this.cmbBaud.Items.AddRange(new object[] {
|
||||||
|
"9600",
|
||||||
|
"12800",
|
||||||
|
"25600",
|
||||||
|
"38400",
|
||||||
|
"512000"});
|
||||||
|
this.cmbBaud.Location = new System.Drawing.Point(0, 28);
|
||||||
|
this.cmbBaud.Name = "cmbBaud";
|
||||||
|
this.cmbBaud.Size = new System.Drawing.Size(134, 28);
|
||||||
|
this.cmbBaud.TabIndex = 0;
|
||||||
|
this.cmbBaud.Text = "9600";
|
||||||
|
//
|
||||||
|
// cmbPOrt
|
||||||
|
//
|
||||||
|
this.cmbPOrt.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
this.cmbPOrt.Font = new System.Drawing.Font("굴림", 15F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.cmbPOrt.FormattingEnabled = true;
|
||||||
|
this.cmbPOrt.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.cmbPOrt.Name = "cmbPOrt";
|
||||||
|
this.cmbPOrt.Size = new System.Drawing.Size(134, 28);
|
||||||
|
this.cmbPOrt.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// panel1
|
||||||
|
//
|
||||||
|
this.panel1.Controls.Add(this.tableLayoutPanel1);
|
||||||
|
this.panel1.Controls.Add(this.cmbBaud);
|
||||||
|
this.panel1.Controls.Add(this.cmbPOrt);
|
||||||
|
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.panel1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.panel1.Name = "panel1";
|
||||||
|
this.panel1.Size = new System.Drawing.Size(134, 96);
|
||||||
|
this.panel1.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel1
|
||||||
|
//
|
||||||
|
this.tableLayoutPanel1.ColumnCount = 2;
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.lbrx, 0, 0);
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.lbtx, 1, 0);
|
||||||
|
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 56);
|
||||||
|
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
|
this.tableLayoutPanel1.RowCount = 1;
|
||||||
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(134, 40);
|
||||||
|
this.tableLayoutPanel1.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// lbrx
|
||||||
|
//
|
||||||
|
this.lbrx.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.lbrx.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lbrx.Font = new System.Drawing.Font("Microsoft Sans Serif", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.lbrx.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.lbrx.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.lbrx.Name = "lbrx";
|
||||||
|
this.lbrx.Size = new System.Drawing.Size(67, 40);
|
||||||
|
this.lbrx.TabIndex = 2;
|
||||||
|
this.lbrx.Text = "RX";
|
||||||
|
this.lbrx.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
|
//
|
||||||
|
// lbtx
|
||||||
|
//
|
||||||
|
this.lbtx.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.lbtx.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lbtx.Font = new System.Drawing.Font("Microsoft Sans Serif", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.lbtx.Location = new System.Drawing.Point(67, 0);
|
||||||
|
this.lbtx.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.lbtx.Name = "lbtx";
|
||||||
|
this.lbtx.Size = new System.Drawing.Size(67, 40);
|
||||||
|
this.lbtx.TabIndex = 3;
|
||||||
|
this.lbtx.Text = "TX";
|
||||||
|
this.lbtx.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
|
//
|
||||||
|
// button1
|
||||||
|
//
|
||||||
|
this.button1.Dock = System.Windows.Forms.DockStyle.Right;
|
||||||
|
this.button1.FlatAppearance.BorderColor = System.Drawing.Color.DodgerBlue;
|
||||||
|
this.button1.FlatAppearance.BorderSize = 3;
|
||||||
|
this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||||
|
this.button1.Font = new System.Drawing.Font("맑은 고딕", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.button1.Location = new System.Drawing.Point(134, 0);
|
||||||
|
this.button1.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.button1.Name = "button1";
|
||||||
|
this.button1.Size = new System.Drawing.Size(82, 96);
|
||||||
|
this.button1.TabIndex = 3;
|
||||||
|
this.button1.Text = "연결";
|
||||||
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
|
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||||
|
//
|
||||||
|
// SerialConn
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.panel1);
|
||||||
|
this.Controls.Add(this.button1);
|
||||||
|
this.Name = "SerialConn";
|
||||||
|
this.Size = new System.Drawing.Size(216, 96);
|
||||||
|
this.panel1.ResumeLayout(false);
|
||||||
|
this.tableLayoutPanel1.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.ComboBox cmbBaud;
|
||||||
|
private System.Windows.Forms.ComboBox cmbPOrt;
|
||||||
|
private System.Windows.Forms.Panel panel1;
|
||||||
|
private System.Windows.Forms.Button button1;
|
||||||
|
private System.Windows.Forms.Label lbtx;
|
||||||
|
private System.Windows.Forms.Label lbrx;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||||
|
}
|
||||||
|
}
|
||||||
130
AGVEmulator/UC/SerialConn.cs
Normal file
130
AGVEmulator/UC/SerialConn.cs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
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 AGVEmulator
|
||||||
|
{
|
||||||
|
public partial class SerialConn : UserControl
|
||||||
|
{
|
||||||
|
public AR.Dev.RS232 dev { get; set; }
|
||||||
|
public SerialConn()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachEvent()
|
||||||
|
{
|
||||||
|
dev.Message += Dev_Message;
|
||||||
|
}
|
||||||
|
void DetachEvent()
|
||||||
|
{
|
||||||
|
dev.Message -= Dev_Message;
|
||||||
|
}
|
||||||
|
private void Dev_Message(object sender, AR.Dev.RS232.MessageEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.MsgType == AR.Dev.RS232.MessageType.Recv)
|
||||||
|
ToggleRX();
|
||||||
|
else if (e.MsgType == AR.Dev.RS232.MessageType.Send)
|
||||||
|
ToggleTX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPortList(string[] list)
|
||||||
|
{
|
||||||
|
cmbPOrt.Items.Clear();
|
||||||
|
foreach (var item in list)
|
||||||
|
cmbPOrt.Items.Add(item);
|
||||||
|
}
|
||||||
|
public string PortName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return cmbPOrt.Text;
|
||||||
|
}
|
||||||
|
set { this.cmbPOrt.Text = value; }
|
||||||
|
}
|
||||||
|
public int BaudRate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return int.Parse(cmbBaud.Text);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
cmbBaud.Text = value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void ToggleTX()
|
||||||
|
{
|
||||||
|
if (lbtx.BackColor == Color.DeepSkyBlue)
|
||||||
|
lbtx.BackColor = Color.White;
|
||||||
|
else lbtx.BackColor = Color.DeepSkyBlue;
|
||||||
|
}
|
||||||
|
public void ToggleRX()
|
||||||
|
{
|
||||||
|
if (lbrx.BackColor == Color.Lime)
|
||||||
|
lbrx.BackColor = Color.White;
|
||||||
|
else lbrx.BackColor = Color.Lime;
|
||||||
|
}
|
||||||
|
public void Disconnect()
|
||||||
|
{
|
||||||
|
if (this.dev.IsOpen)
|
||||||
|
{
|
||||||
|
DetachEvent();
|
||||||
|
dev.Close();
|
||||||
|
}
|
||||||
|
if (dev.IsOpen)
|
||||||
|
button1.BackColor = Color.Lime;
|
||||||
|
else button1.BackColor = Color.Tomato;
|
||||||
|
}
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
if (this.dev.IsOpen)
|
||||||
|
{
|
||||||
|
DetachEvent();
|
||||||
|
dev.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.dev.PortName = cmbPOrt.Text;
|
||||||
|
this.dev.BaudRate = int.Parse(cmbBaud.Text);
|
||||||
|
AttachEvent();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dev.Open();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
button1.BackColor = Color.Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev.IsOpen)
|
||||||
|
button1.BackColor = Color.Lime;
|
||||||
|
else button1.BackColor = Color.Tomato;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (this.dev == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("시리얼 개체가 연결되지 않았습니다");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(int.TryParse(cmbBaud.Text,out int baud)==false)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Baudrate 값이 올바르지 않습니다");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Connect();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
AGVEmulator/UC/SerialConn.resx
Normal file
120
AGVEmulator/UC/SerialConn.resx
Normal 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>
|
||||||
3
AGVEmulator/app.config
Normal file
3
AGVEmulator/app.config
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
|
||||||
243
AGVEmulator/devBMS.cs
Normal file
243
AGVEmulator/devBMS.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
class DevBMS : AR.Dev.RS232
|
||||||
|
{
|
||||||
|
public class RequestBatteryDataArgs : EventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
public float Volt { get; set; }
|
||||||
|
public float Remain { get; set; }
|
||||||
|
public int CurA { get; set; }
|
||||||
|
public int MaxA { get; set; }
|
||||||
|
public UInt16 Temp1 { get; set; }
|
||||||
|
public UInt16 Temp2 { get; set; }
|
||||||
|
public RequestBatteryDataArgs()
|
||||||
|
{
|
||||||
|
this.Volt = 25;
|
||||||
|
this.Remain = 79f;
|
||||||
|
this.CurA = 40;
|
||||||
|
this.MaxA = 80;
|
||||||
|
Temp1 = 0;
|
||||||
|
Temp2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class RequestVoltageDataArgs : EventArgs
|
||||||
|
{
|
||||||
|
public UInt16[] cellVolt { get; set; }
|
||||||
|
public RequestVoltageDataArgs()
|
||||||
|
{
|
||||||
|
|
||||||
|
cellVolt = new UInt16[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<RequestBatteryDataArgs> RequestBatteryData;
|
||||||
|
public event EventHandler<RequestVoltageDataArgs> RequestVoltageData;
|
||||||
|
public override bool ProcessRecvData(byte[] data)
|
||||||
|
{
|
||||||
|
//dd로 시작하고 34개의 데이터(명령이 0D로 끝나면 34이고, 77로 끝나면 34보다크다)
|
||||||
|
var sample000D = "DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77";
|
||||||
|
var sampleFC77 = "DD 03 00 1B 0A 6E 0D DD 04 00 10 0D 0A 0D 09 0D 0C 0D 09 0D 0A 0D 09 0D 09 0D 0C FF 38 77 DD 04 00 10 0D 0A 0D 09 0D 0C 0D 09 0D 0A 0D 09 0D 09 0D 0C FF 38 77";
|
||||||
|
var sampleFD77 = "DD 03 00 1B 0A 6E 00 00 1F 4A 29 04 00 4E 2C D7 00 00 00 00 00 00 28 4C 03 08 02 0B 9F 0B A9 FB A7 77 DD 03 00 1B 0A 6E 00 00 1F 4A 29 04 00 4E 2C D7 00 00 00 00 00 00 28 4C 03 08 02 0B 9F 0B AA FB A6 77";
|
||||||
|
|
||||||
|
//basic
|
||||||
|
var basicinfosmaple = "0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66";
|
||||||
|
var basic_payload = basicinfosmaple.Split(' ').ToList().Select(t => Convert.ToByte(t, 16)).ToArray(); //old
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var barr0D = sample000D.Split(' ').ToList().Select(t => Convert.ToByte(t, 16)).ToArray(); //old
|
||||||
|
var barrFC77 = sampleFC77.Split(' ').ToList().Select(t => Convert.ToByte(t, 16)).ToArray(); //setting
|
||||||
|
var barrFD77 = sampleFD77.Split(' ').ToList().Select(t => Convert.ToByte(t, 16)).ToArray(); //normal
|
||||||
|
|
||||||
|
if (data.First() != 0xDD || data.Last() != 0x77)
|
||||||
|
{
|
||||||
|
RaiseMessage(MessageType.Error, $"stx,etx error data={data.HexString()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var sts = data[1];
|
||||||
|
var cmd = data[2];
|
||||||
|
|
||||||
|
RaiseMessage(MessageType.Normal, "Rx:" + data.HexString());
|
||||||
|
byte sendOld = 0;
|
||||||
|
|
||||||
|
if (cmd == 0x03) //get basic info
|
||||||
|
{
|
||||||
|
if (RequestBatteryData != null)
|
||||||
|
{
|
||||||
|
var p = new RequestBatteryDataArgs();
|
||||||
|
RequestBatteryData.Invoke(this, p);
|
||||||
|
|
||||||
|
//전압
|
||||||
|
var Volt = (UInt16)(p.Volt * 100);
|
||||||
|
var arr_volt = BitConverter.GetBytes(Volt).Reverse().ToArray();
|
||||||
|
Array.Copy(arr_volt, 0, basic_payload, 0, arr_volt.Length);
|
||||||
|
|
||||||
|
//잔량A
|
||||||
|
var CurA = (UInt16)p.CurA;
|
||||||
|
var arr_cura = BitConverter.GetBytes(CurA).Reverse().ToArray();
|
||||||
|
Array.Copy(arr_cura, 0, basic_payload, 4, arr_cura.Length);
|
||||||
|
|
||||||
|
|
||||||
|
//최대A
|
||||||
|
var MaxA = (UInt16)p.MaxA;
|
||||||
|
var arr_maxa = BitConverter.GetBytes(MaxA).Reverse().ToArray();
|
||||||
|
Array.Copy(arr_maxa, 0, basic_payload, 6, arr_maxa.Length);
|
||||||
|
|
||||||
|
//%
|
||||||
|
var perc = (byte)(int)p.Remain;
|
||||||
|
Array.Copy(new byte[] { perc }, 0, basic_payload, 19, 1);
|
||||||
|
|
||||||
|
//temp1
|
||||||
|
var temp1 = BitConverter.GetBytes((UInt16)(p.Temp1 + 2731)).Reverse().ToArray();
|
||||||
|
Array.Copy(temp1, 0, basic_payload, 23,2);
|
||||||
|
|
||||||
|
//temp2
|
||||||
|
var temp2 = BitConverter.GetBytes((UInt16)(p.Temp2 + 2731)).Reverse().ToArray();
|
||||||
|
Array.Copy(temp2, 0, basic_payload, 25, 2);
|
||||||
|
|
||||||
|
var datalen = (byte)basic_payload.Length;
|
||||||
|
var payload_cs = new List<byte>();
|
||||||
|
payload_cs.Add(datalen);
|
||||||
|
payload_cs.AddRange(basic_payload);
|
||||||
|
var chks = MakeCheckSum(payload_cs);
|
||||||
|
|
||||||
|
var buffer = new List<byte>();
|
||||||
|
buffer.Add(0xdd);
|
||||||
|
buffer.Add(cmd);
|
||||||
|
buffer.Add(0x00);
|
||||||
|
buffer.Add(datalen);
|
||||||
|
buffer.AddRange(basic_payload);
|
||||||
|
buffer.AddRange(chks);
|
||||||
|
buffer.Add(0x77);
|
||||||
|
|
||||||
|
|
||||||
|
////makechecksum
|
||||||
|
//UInt16 chksum = 0;
|
||||||
|
//for (int i = 3; i <= 19; i++)
|
||||||
|
//{
|
||||||
|
// chksum += barrFD77[i];
|
||||||
|
//}
|
||||||
|
//chksum = (UInt16)(0xFFFF - chksum + 1);
|
||||||
|
//Array.Copy(BitConverter.GetBytes(chksum).Reverse().ToArray(), 0, barrFD77, 20, 2);
|
||||||
|
|
||||||
|
|
||||||
|
RaiseMessage(MessageType.Normal, $"Volt:{p.Volt}v,Remain:{p.Remain}%,temp1:{p.Temp1/10f},Temp2:{p.Temp2/10f}");
|
||||||
|
|
||||||
|
var sendstr = System.Text.Encoding.Default.GetString(barr0D);
|
||||||
|
RaiseMessage(MessageType.Normal, "Tx:" + barr0D.HexString());
|
||||||
|
WriteData(buffer.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cmd == 0x04) //get cellvoltage
|
||||||
|
{
|
||||||
|
if (RequestVoltageData != null)
|
||||||
|
{
|
||||||
|
var p = new RequestVoltageDataArgs();
|
||||||
|
RequestVoltageData.Invoke(this, p);
|
||||||
|
|
||||||
|
//var sig = data.Skip(data.Length - 2).Take(1).First();
|
||||||
|
|
||||||
|
//cellvolt 240201
|
||||||
|
var payload = new byte[8 * 2];
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
var volt = p.cellVolt[i];
|
||||||
|
var arr_cellvolt = BitConverter.GetBytes(volt).Reverse().ToArray();
|
||||||
|
Array.Copy(arr_cellvolt, 0, payload, (i * 2), arr_cellvolt.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
//makechecksum
|
||||||
|
var datalen = (byte)payload.Length;
|
||||||
|
var payload_cs = new List<byte>();
|
||||||
|
payload_cs.Add(datalen);
|
||||||
|
payload_cs.AddRange(payload);
|
||||||
|
var chksum = MakeCheckSum(payload_cs);
|
||||||
|
|
||||||
|
var buffer = new List<byte>();
|
||||||
|
buffer.Add(0xDD);
|
||||||
|
buffer.Add(cmd);
|
||||||
|
buffer.Add(0x00);
|
||||||
|
buffer.Add(datalen);
|
||||||
|
buffer.AddRange(payload);
|
||||||
|
buffer.AddRange(chksum);
|
||||||
|
buffer.Add(0x77);
|
||||||
|
WriteData(buffer.ToArray());
|
||||||
|
RaiseMessage(MessageType.Normal, "Tx:" + buffer.ToArray().HexString());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public byte[] MakeCheckSum(IEnumerable<byte> payload)
|
||||||
|
{
|
||||||
|
UInt16 retval = 0;
|
||||||
|
var payloadsum = payload.Sum(t => t);
|
||||||
|
retval = (UInt16)(payloadsum);
|
||||||
|
retval = (UInt16)(0xFFFF - retval);
|
||||||
|
retval += 1;
|
||||||
|
|
||||||
|
return BitConverter.GetBytes(retval).Reverse().ToArray();
|
||||||
|
}
|
||||||
|
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
|
||||||
|
{
|
||||||
|
//DD A5 03 00 FF FD 77 0D
|
||||||
|
//remainBuffer = new byte[] { };
|
||||||
|
List<byte> remain = new List<byte>();
|
||||||
|
bool retval = false;
|
||||||
|
foreach (var b in buf)
|
||||||
|
{
|
||||||
|
if (retval)
|
||||||
|
{
|
||||||
|
remain.Add(b);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b == 0xDD) //stx
|
||||||
|
{
|
||||||
|
tempBuffer.Clear();
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
}
|
||||||
|
else if (b == 0x0D || b == 0x77) //etx
|
||||||
|
{
|
||||||
|
//과거데이터 34개이다 (DD A5 03 00 FF FD 77 0D)
|
||||||
|
//대쉬보드상태일때(DD A5 03 00 FF FD 77)
|
||||||
|
//설정상태일때-셀전압표시(DD A5 04 00 FF FC 77)
|
||||||
|
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
retval = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tempBuffer.Count > 0 && tempBuffer[0] == 0xDD)
|
||||||
|
{
|
||||||
|
tempBuffer.Add(b);
|
||||||
|
if (tempBuffer.Count > 10)
|
||||||
|
{
|
||||||
|
RaiseMessage(MessageType.Error, "buffer over");
|
||||||
|
tempBuffer.Clear();
|
||||||
|
retval = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//trash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remainBuffer = remain.ToArray();
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1711
AGVEmulator/fMain.Designer.cs
generated
Normal file
1711
AGVEmulator/fMain.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
943
AGVEmulator/fMain.cs
Normal file
943
AGVEmulator/fMain.cs
Normal file
@@ -0,0 +1,943 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Forms.VisualStyles;
|
||||||
|
using static AGVEmulator.DevAGV;
|
||||||
|
using AGVNavigationCore.Controls;
|
||||||
|
using AGVNavigationCore.Models;
|
||||||
|
|
||||||
|
namespace AGVEmulator
|
||||||
|
{
|
||||||
|
public partial class fMain : Form
|
||||||
|
{
|
||||||
|
arUtil.Log logAGV, logBMS, logCAL;
|
||||||
|
DevBMS BMS;
|
||||||
|
DevAGV AGV;
|
||||||
|
DevXBE XBE;
|
||||||
|
|
||||||
|
// Map Control
|
||||||
|
private UnifiedAGVCanvas _agvCanvas;
|
||||||
|
private VirtualAGV _visualAgv;
|
||||||
|
|
||||||
|
// Emulator State
|
||||||
|
private MapNode _targetNode = null;
|
||||||
|
private bool _isTurning = false;
|
||||||
|
private double _targetAngle = 0;
|
||||||
|
private PointF _currentPosF; // For smooth movement
|
||||||
|
|
||||||
|
float BMS_MaxA = 80f;
|
||||||
|
float BMS_Remain = 79f;
|
||||||
|
float BMS_CurA = 70f;
|
||||||
|
float BMS_Volt = 25.4f;
|
||||||
|
|
||||||
|
UInt16[] cellvolt = new UInt16[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
|
||||||
|
public fMain()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
this.Text = $"{Application.ProductName} ver.{Application.ProductVersion}";
|
||||||
|
// logPLC = new arUtil.Log();
|
||||||
|
logAGV = new arUtil.Log();
|
||||||
|
logBMS = new arUtil.Log();
|
||||||
|
logCAL = new arUtil.Log();
|
||||||
|
|
||||||
|
// logPLC.FileNameFormat = "{yyyyMMdd}_PLC";
|
||||||
|
logAGV.FileNameFormat = "{yyyyMMdd}_AGV";
|
||||||
|
logBMS.FileNameFormat = "{yyyyMMdd}_BMS";
|
||||||
|
logCAL.FileNameFormat = "{yyyyMMdd}_CAL";
|
||||||
|
|
||||||
|
// logPLC.RaiseMsg += (s1, e1, d1) => { this.rtPLC.AddMsg(s1, e1, d1); };
|
||||||
|
logAGV.RaiseMsg += (s1, e1, d1) =>
|
||||||
|
{
|
||||||
|
if (d1.StartsWith("Tx"))
|
||||||
|
this.rtAGV.AddMsg(s1, e1, d1);
|
||||||
|
else
|
||||||
|
this.rtAGVPro.AddMsg(s1, e1, d1);
|
||||||
|
};
|
||||||
|
logBMS.RaiseMsg += (s1, e1, d1) => { this.rtBMS.AddMsg(s1, e1, d1); };
|
||||||
|
logCAL.RaiseMsg += (s1, e1, d1) => { this.rtCAL.AddMsg(s1, e1, d1); };
|
||||||
|
|
||||||
|
|
||||||
|
var logcolor = new arCtl.sLogMessageColor[] {
|
||||||
|
new arCtl.sLogMessageColor("NORM", Color.White),
|
||||||
|
new arCtl.sLogMessageColor("INF", Color.SkyBlue),
|
||||||
|
new arCtl.sLogMessageColor("WARN", Color.Tomato),
|
||||||
|
new arCtl.sLogMessageColor("ERR", Color.Red),
|
||||||
|
new arCtl.sLogMessageColor("RX", Color.Lime),
|
||||||
|
new arCtl.sLogMessageColor("TX", Color.Orange),
|
||||||
|
};
|
||||||
|
|
||||||
|
//rtPLC.ColorList = logcolor;
|
||||||
|
rtAGV.ColorList = logcolor;
|
||||||
|
rtBMS.ColorList = logcolor;
|
||||||
|
rtCAL.ColorList = logcolor;
|
||||||
|
rtAGVPro.ColorList = logcolor;
|
||||||
|
|
||||||
|
//this.FormClosed += Form1_FormClosed;
|
||||||
|
|
||||||
|
BMS = new DevBMS();
|
||||||
|
AGV = new DevAGV();
|
||||||
|
XBE = new DevXBE();
|
||||||
|
this.serAGV.dev = AGV;
|
||||||
|
this.serBMS.dev = BMS;
|
||||||
|
this.serCAL.dev = XBE;
|
||||||
|
|
||||||
|
BMS.Message += BMS_Message;
|
||||||
|
AGV.Message += AGV_Message;
|
||||||
|
XBE.Message += CAL_Message;
|
||||||
|
|
||||||
|
BMS.RequestBatteryData += Bms_RequestBatteryData;
|
||||||
|
BMS.RequestVoltageData += BMS_RequestVoltageData;
|
||||||
|
XBE.ProtocReceived += CAL_ProtocReceived;
|
||||||
|
AGV.RequestStatusData += Agv_RequestStatusData;
|
||||||
|
AGV.ValueChanged += Agv_ValueChanged;
|
||||||
|
AGV.StsValueChanged += Agv_StsValueChanged;
|
||||||
|
AGV.Command += Agv_Command;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random rnd;
|
||||||
|
private void Form1_Load(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
rnd = new Random(3000);
|
||||||
|
|
||||||
|
serAGV.BaudRate = 57600;
|
||||||
|
// serPLC.BaudRate = 57600;
|
||||||
|
trackBar1_Scroll(null, null);
|
||||||
|
trbT2_Scroll(null, null);
|
||||||
|
trbT1_Scroll(null, null);
|
||||||
|
timer1.Start();
|
||||||
|
|
||||||
|
//plc inout 이름 설정
|
||||||
|
List<string> titles = new List<string>();
|
||||||
|
List<bool> values = new List<bool>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var arrs = Enum.GetNames(typeof(DevAGV.esystemflag0));
|
||||||
|
foreach (var item in arrs)
|
||||||
|
{
|
||||||
|
var data = (DevAGV.esystemflag0)Enum.Parse(typeof(DevAGV.esystemflag0), item);
|
||||||
|
|
||||||
|
var chk = new CheckBox();
|
||||||
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
|
chk.AutoSize = true;
|
||||||
|
chk.Visible = true;
|
||||||
|
chk.Tag = (int)data;
|
||||||
|
chk.Dock = DockStyle.Top;
|
||||||
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
|
this.panel6.Controls.Add(chk);
|
||||||
|
chk.Checked = true; //기본값 rue
|
||||||
|
}
|
||||||
|
arrs = Enum.GetNames(typeof(DevAGV.esystemflag1));
|
||||||
|
foreach (var item in arrs)
|
||||||
|
{
|
||||||
|
var data = (DevAGV.esystemflag1)Enum.Parse(typeof(DevAGV.esystemflag1), item);
|
||||||
|
|
||||||
|
var chk = new CheckBox();
|
||||||
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
|
chk.AutoSize = true;
|
||||||
|
chk.Visible = true;
|
||||||
|
chk.Tag = (int)data;
|
||||||
|
chk.Dock = DockStyle.Top;
|
||||||
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
|
this.panel7.Controls.Add(chk);
|
||||||
|
}
|
||||||
|
arrs = Enum.GetNames(typeof(DevAGV.esignal));
|
||||||
|
foreach (var item in arrs)
|
||||||
|
{
|
||||||
|
var data = (DevAGV.esignal)Enum.Parse(typeof(DevAGV.esignal), item);
|
||||||
|
|
||||||
|
var chk = new CheckBox();
|
||||||
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
|
chk.AutoSize = true;
|
||||||
|
chk.Visible = true;
|
||||||
|
chk.Tag = (int)data;
|
||||||
|
chk.Dock = DockStyle.Top;
|
||||||
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
|
this.panel8.Controls.Add(chk);
|
||||||
|
}
|
||||||
|
arrs = Enum.GetNames(typeof(DevAGV.eerror));
|
||||||
|
foreach (var item in arrs)
|
||||||
|
{
|
||||||
|
var data = (DevAGV.eerror)Enum.Parse(typeof(DevAGV.eerror), item);
|
||||||
|
|
||||||
|
var chk = new CheckBox();
|
||||||
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
|
chk.AutoSize = true;
|
||||||
|
chk.Visible = true;
|
||||||
|
chk.Tag = (int)data;
|
||||||
|
chk.Dock = DockStyle.Top;
|
||||||
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
|
this.panel9.Controls.Add(chk);
|
||||||
|
}
|
||||||
|
|
||||||
|
//기본 중지상태로 변환
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.agv_stop, true);
|
||||||
|
|
||||||
|
foreach (RadioButton rad in groupBox5.Controls)
|
||||||
|
{
|
||||||
|
rad.CheckedChanged += (s1, e1) =>
|
||||||
|
{
|
||||||
|
if (rad.Checked)
|
||||||
|
{
|
||||||
|
AGV.sts_dir = rad.Text[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
foreach (RadioButton rad in groupBox2.Controls)
|
||||||
|
{
|
||||||
|
rad.CheckedChanged += (s1, e1) =>
|
||||||
|
{
|
||||||
|
if (rad.Checked)
|
||||||
|
{
|
||||||
|
AGV.sts_bunki = rad.Text[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
foreach (RadioButton rad in groupBox4.Controls)
|
||||||
|
{
|
||||||
|
rad.CheckedChanged += (s1, e1) =>
|
||||||
|
{
|
||||||
|
if (rad.Checked)
|
||||||
|
{
|
||||||
|
AGV.sts_speed = rad.Text[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
foreach (RadioButton rad in groupBox8.Controls)
|
||||||
|
{
|
||||||
|
rad.CheckedChanged += (s1, e1) =>
|
||||||
|
{
|
||||||
|
if (rad.Checked)
|
||||||
|
{
|
||||||
|
AGV.sts_sensor = rad.Text[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeViewer();
|
||||||
|
InitializeMapControl();
|
||||||
|
|
||||||
|
|
||||||
|
agvViewer1.TagTouched += AgvViewer1_TagTouched;
|
||||||
|
agvViewer1.MarkTouched += AgvViewer1_MarkTouched;
|
||||||
|
agvViewer1.Command += AgvViewer1_Command;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeViewer()
|
||||||
|
{
|
||||||
|
//태그목록
|
||||||
|
List<UC.AgvViewer.ptdata> listTAG = new List<UC.AgvViewer.ptdata>();
|
||||||
|
//var strtag = new string[] {
|
||||||
|
// "9000", "9001", //not
|
||||||
|
// "9010", "9011",
|
||||||
|
// //"9020", "9021",
|
||||||
|
// "9030", "9031",
|
||||||
|
// // "9040", "9041",
|
||||||
|
// "9050", "9051",
|
||||||
|
// // "9060", "9061",
|
||||||
|
// "9070", "9071",
|
||||||
|
// "9080", "9081",
|
||||||
|
// "9090", "9091"};
|
||||||
|
|
||||||
|
//foreach(var tag in strtag)
|
||||||
|
//{
|
||||||
|
// listTAG.Add(new UC.AgvViewer.ptdata {
|
||||||
|
// active=false,
|
||||||
|
// data = tag,
|
||||||
|
// pos =
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
//agvViewer1.listTAG = listTAG.ToArray();
|
||||||
|
|
||||||
|
//마크지점
|
||||||
|
List<UC.AgvViewer.ptdata> listMRK = new List<UC.AgvViewer.ptdata>();
|
||||||
|
var strmark = new string[] { "NOT", "QC", "CHG", "{0}", "#1", "{1}", "#2", "{2}", "#3", "{3}", "#4", "POT" };
|
||||||
|
var pos = 50;
|
||||||
|
var tagstart = 9000;
|
||||||
|
foreach (var item in strmark)
|
||||||
|
{
|
||||||
|
listMRK.Add(new UC.AgvViewer.ptdata
|
||||||
|
{
|
||||||
|
pos = pos,
|
||||||
|
data = item,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (item.Equals("CHG"))
|
||||||
|
{
|
||||||
|
pos += 100;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (item.Equals("{0}")) tagstart = 9350;
|
||||||
|
else if (item.Equals("{1}")) tagstart = 9450;
|
||||||
|
else if (item.Equals("{2}")) tagstart = 9550;
|
||||||
|
else if (item.Equals("{3}")) tagstart = 9650;
|
||||||
|
else if (item.Equals("#1")) tagstart = 9400;
|
||||||
|
else if (item.Equals("#2")) tagstart = 9500;
|
||||||
|
else if (item.Equals("#3")) tagstart = 9600;
|
||||||
|
else if (item.Equals("#4")) tagstart = 9700;
|
||||||
|
|
||||||
|
|
||||||
|
//지정위치 좌우에 태그를 심는다
|
||||||
|
listTAG.Add(new UC.AgvViewer.ptdata
|
||||||
|
{
|
||||||
|
pos = pos - 20,
|
||||||
|
data = tagstart.ToString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (item.Equals("NOT") == false && item.Equals("POT") == false && item.StartsWith("{") == false)
|
||||||
|
tagstart += 1;
|
||||||
|
|
||||||
|
|
||||||
|
listTAG.Add(new UC.AgvViewer.ptdata
|
||||||
|
{
|
||||||
|
pos = pos + 20,
|
||||||
|
data = tagstart.ToString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
pos += 100;
|
||||||
|
|
||||||
|
if (item.Equals("NOT") == false && item.Equals("POT") == false && item.StartsWith("{") == false)
|
||||||
|
tagstart += 99;
|
||||||
|
else
|
||||||
|
tagstart += 100;
|
||||||
|
|
||||||
|
|
||||||
|
if (item.Equals("NOT"))
|
||||||
|
tagstart = 9300;
|
||||||
|
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// pos += 100;
|
||||||
|
// tagstart += 10;
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
agvViewer1.listMRK = listMRK.ToArray();
|
||||||
|
agvViewer1.listTAG = listTAG.ToArray();
|
||||||
|
agvViewer1.Invalidate();
|
||||||
|
|
||||||
|
}
|
||||||
|
private void AgvViewer1_Command(object sender, UC.AgvViewer.TagArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data == "stop")
|
||||||
|
{
|
||||||
|
AGV.SetAGV(esystemflag1.agv_run, false);
|
||||||
|
AGV.SetAGV(esystemflag1.agv_stop, true);
|
||||||
|
logAGV.Add("시뮬로부터 자동 중지");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AgvViewer1_MarkTouched(object sender, UC.AgvViewer.TagArgs e)
|
||||||
|
{
|
||||||
|
// throw new NotImplementedException();
|
||||||
|
AGV.SetAGV(esignal.mark_sensor_1, e.Active);
|
||||||
|
logAGV.Add($"mark {e.Data} touch:{e.Active}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AgvViewer1_TagTouched(object sender, UC.AgvViewer.TagArgs e)
|
||||||
|
{
|
||||||
|
logAGV.Add($"tag touch:{e.Data}");
|
||||||
|
numericUpDown1.Text = e.Data;// decimal.Parse(e.Data);
|
||||||
|
button18.PerformClick();
|
||||||
|
|
||||||
|
UpdateVisualAgvPosition(e.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeMapControl()
|
||||||
|
{
|
||||||
|
_agvCanvas = new UnifiedAGVCanvas();
|
||||||
|
_agvCanvas.Dock = DockStyle.Fill;
|
||||||
|
_agvCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Emulator; // Enable Emulator Mode
|
||||||
|
_agvCanvas.NodeRightClicked += _agvCanvas_NodeRightClicked;
|
||||||
|
this.tabPage4.Controls.Add(_agvCanvas);
|
||||||
|
|
||||||
|
// Load Map
|
||||||
|
// Try to find map file in standard location
|
||||||
|
string mapDir = Path.GetFullPath(Path.Combine(Application.StartupPath, @"..\..\..\..\Cs_HMI\Data"));
|
||||||
|
string mapPath = Path.Combine(mapDir, "NewMap.agvmap");
|
||||||
|
|
||||||
|
if (!File.Exists(mapPath))
|
||||||
|
{
|
||||||
|
if (Directory.Exists(mapDir))
|
||||||
|
{
|
||||||
|
var files = Directory.GetFiles(mapDir, "*.agvmap");
|
||||||
|
if (files.Length > 0) mapPath = files[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(mapPath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mapresult = MapLoader.LoadMapFromFile(mapPath);
|
||||||
|
_agvCanvas.SetMapLoadResult(mapresult);//.Nodes = _mapNodes;
|
||||||
|
|
||||||
|
// Initialize Visual AGV
|
||||||
|
if (_agvCanvas.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
_visualAgv = new VirtualAGV("AGV01", _agvCanvas.Nodes[0].Position);
|
||||||
|
_agvCanvas.AGVList = new List<IAGV> { _visualAgv };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logAGV.Add($"Map Load Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateVisualAgvPosition(string tag)
|
||||||
|
{
|
||||||
|
if (_visualAgv == null || _agvCanvas.Nodes == null) return;
|
||||||
|
|
||||||
|
// Find node by tag
|
||||||
|
// Assuming NodeId might be the tag or contain it
|
||||||
|
var node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == tag || n.Id.EndsWith(tag));
|
||||||
|
|
||||||
|
// If not found, try to parse tag as int and match
|
||||||
|
if (node == null && int.TryParse(tag, out int tagNum))
|
||||||
|
{
|
||||||
|
node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == tagNum.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
_visualAgv.SetPosition(node, (AGV.sts_dir == 'F' ? AgvDirection.Forward : AgvDirection.Backward));
|
||||||
|
UpdateVisualAGV();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char GetGroupItemCheckbox(GroupBox grp)
|
||||||
|
{
|
||||||
|
foreach (var ctl in grp.Controls)
|
||||||
|
{
|
||||||
|
if (ctl is RadioButton)
|
||||||
|
{
|
||||||
|
var rad = ctl as RadioButton;
|
||||||
|
if (rad.Checked) return rad.Text[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int PLC_LeftPosition = 0;
|
||||||
|
int PLC_RightPosition = 0;
|
||||||
|
int PLC_LeftDir = 0;
|
||||||
|
int PLC_RightDir = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public void SetBit(ref UInt16 _value, int idx, Boolean value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
var offset = (UInt16)(1 << idx);
|
||||||
|
_value = (UInt16)(_value | offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var offset = (UInt16)(~(1 << idx));
|
||||||
|
_value = (UInt16)(_value & offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void SetBit(ref byte _value, int idx, Boolean value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
var offset = (byte)(1 << idx);
|
||||||
|
_value = (byte)(_value | offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var offset = (byte)(~(1 << idx));
|
||||||
|
_value = (byte)(_value & offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void aaplycheckboxbit(ref UInt16 v, Panel p)
|
||||||
|
{
|
||||||
|
foreach (CheckBox chk in p.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(chk.Tag.ToString());
|
||||||
|
SetBit(ref v, idx, chk.Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void aaplycheckboxbit(ref byte v, Panel p)
|
||||||
|
{
|
||||||
|
foreach (CheckBox chk in p.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(chk.Tag.ToString());
|
||||||
|
SetBit(ref v, idx, chk.Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void button5_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
AGV.WriteData("ACK");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button4_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
AGV.WriteData("NAK");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void trackBar1_Scroll(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
BMS_MaxA = float.Parse(label4.Text);
|
||||||
|
BMS_CurA = (float)(trackBar1.Value / 100f);
|
||||||
|
BMS_Remain = 100f * (BMS_CurA / BMS_MaxA);// (trackBar1.Value / 10f);
|
||||||
|
var minvolt = 20.2f;
|
||||||
|
var maxvolt = 26.8f;
|
||||||
|
// BMS_CurA = (BMS_MaxA * (BMS_Remain / 100f));
|
||||||
|
label3.Text = BMS_CurA.ToString("N0"); //curr amp
|
||||||
|
label5.Text = $"{BMS_Remain:N2}%";
|
||||||
|
BMS_Volt = minvolt + ((maxvolt - minvolt) * (BMS_CurA / BMS_MaxA));
|
||||||
|
label6.Text = $"{BMS_Volt:N2}v";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer1_Tick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
timer1.Stop();
|
||||||
|
sbAGV.ForeColor = AGV.IsOpen ? Color.ForestGreen : Color.Red;
|
||||||
|
// sbPLC.ForeColor = PLC.IsOpen ? Color.ForestGreen : Color.Red;
|
||||||
|
sbBMS.ForeColor = BMS.IsOpen ? Color.ForestGreen : Color.Red;
|
||||||
|
sbCAL.ForeColor = XBE.IsOpen ? Color.ForestGreen : Color.Red;
|
||||||
|
|
||||||
|
//if (checkBox2.Checked && PLC != null && PLC.IsOpen) PLC.AutoSendData(); //자동전송해야함
|
||||||
|
if (checkBox3.Checked && AGV != null && AGV.IsOpen) AGV.AutoSendData(); //자동전송해야함
|
||||||
|
if (BMS != null && BMS.IsOpen) BMS.AutoSendData(); //자동전송해야함
|
||||||
|
if (XBE != null && XBE.IsOpen) XBE.AutoSendData(); //자동전송해야함
|
||||||
|
|
||||||
|
if (chkSimulation.Checked && AGV.GetAGV(esystemflag1.agv_run))
|
||||||
|
{
|
||||||
|
if (radioButton15.Checked)
|
||||||
|
{
|
||||||
|
agvViewer1.dir = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
agvViewer1.dir = -1;
|
||||||
|
}
|
||||||
|
if (radioButton12.Checked) agvViewer1.mspd = 7;
|
||||||
|
else if (radioButton11.Checked) agvViewer1.mspd = 20;
|
||||||
|
else agvViewer1.mspd = 40;
|
||||||
|
agvViewer1.Invalidate();
|
||||||
|
|
||||||
|
UpdateEmulatorLogic();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
timer1.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateVisualAGV()
|
||||||
|
{
|
||||||
|
if (_visualAgv != null)
|
||||||
|
{
|
||||||
|
_agvCanvas.UpdateAGVPosition(_visualAgv.AgvId, _visualAgv.CurrentPosition);
|
||||||
|
// Update direction if needed, though UnifiedAGVCanvas uses Enum
|
||||||
|
// _agvCanvas.UpdateAGVDirection(_visualAgv.AgvId, _visualAgv.CurrentDirection);
|
||||||
|
_agvCanvas.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEmulatorLogic()
|
||||||
|
{
|
||||||
|
if (_visualAgv == null || _agvCanvas.Nodes == null) return;
|
||||||
|
|
||||||
|
// Initialize float position if needed
|
||||||
|
if (_currentPosF.IsEmpty) _currentPosF = _visualAgv.CurrentPosition;
|
||||||
|
|
||||||
|
// Movement Logic
|
||||||
|
double speed = (agvViewer1.mspd > 0 ? agvViewer1.mspd : 10) * 0.2; // Scale speed
|
||||||
|
|
||||||
|
if (_isTurning)
|
||||||
|
{
|
||||||
|
// Turn Logic: 90 deg in 5 sec = 18 deg/sec
|
||||||
|
// Timer is likely 100ms? (Default WinForms timer) -> 1.8 deg/tick
|
||||||
|
double turnSpeed = 1.8;
|
||||||
|
|
||||||
|
double diff = _targetAngle - _visualAgv.Angle;
|
||||||
|
// Normalize diff to -180 to 180
|
||||||
|
while (diff > 180) diff -= 360;
|
||||||
|
while (diff <= -180) diff += 360;
|
||||||
|
|
||||||
|
if (Math.Abs(diff) < turnSpeed)
|
||||||
|
{
|
||||||
|
_visualAgv.Angle = _targetAngle;
|
||||||
|
_isTurning = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_visualAgv.Angle += Math.Sign(diff) * turnSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move Forward
|
||||||
|
double rad = _visualAgv.Angle * Math.PI / 180.0;
|
||||||
|
_currentPosF.X += (float)(speed * Math.Cos(rad));
|
||||||
|
_currentPosF.Y += (float)(speed * Math.Sin(rad));
|
||||||
|
|
||||||
|
_visualAgv.CurrentPosition = Point.Round(_currentPosF);
|
||||||
|
|
||||||
|
// Check for Nodes (RFID Trigger)
|
||||||
|
foreach (var node in _agvCanvas.Nodes)
|
||||||
|
{
|
||||||
|
double dist = Math.Sqrt(Math.Pow(node.Position.X - _visualAgv.CurrentPosition.X, 2) + Math.Pow(node.Position.Y - _visualAgv.CurrentPosition.Y, 2));
|
||||||
|
if (dist < 15) // Hit Node
|
||||||
|
{
|
||||||
|
// Send Tag
|
||||||
|
if (node.Id != numericUpDown1.Text)
|
||||||
|
{
|
||||||
|
if (int.TryParse(node.Id, out int tag))
|
||||||
|
{
|
||||||
|
AGV.SendTag(node.Id);
|
||||||
|
numericUpDown1.Text = node.Id;
|
||||||
|
|
||||||
|
// Snap to node
|
||||||
|
_currentPosF = node.Position;
|
||||||
|
_visualAgv.CurrentPosition = node.Position;
|
||||||
|
|
||||||
|
// Decide Next Move (Turn/Straight)
|
||||||
|
DecideNextMove(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateVisualAGV();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DecideNextMove(MapNode currentNode)
|
||||||
|
{
|
||||||
|
// Simple logic:
|
||||||
|
// If AGV.sts_bunki == 'L', turn -90
|
||||||
|
// If AGV.sts_bunki == 'R', turn +90
|
||||||
|
// Else keep straight
|
||||||
|
|
||||||
|
if (AGV.sts_bunki == 'L')
|
||||||
|
{
|
||||||
|
_targetAngle = _visualAgv.Angle - 90;
|
||||||
|
_isTurning = true;
|
||||||
|
}
|
||||||
|
else if (AGV.sts_bunki == 'R')
|
||||||
|
{
|
||||||
|
_targetAngle = _visualAgv.Angle + 90;
|
||||||
|
_isTurning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize Target Angle
|
||||||
|
while (_targetAngle >= 360) _targetAngle -= 360;
|
||||||
|
while (_targetAngle < 0) _targetAngle += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _agvCanvas_NodeRightClicked(object sender, NodeBase e)
|
||||||
|
{
|
||||||
|
if (e != null && _visualAgv != null)
|
||||||
|
{
|
||||||
|
_visualAgv.CurrentPosition = e.Position;
|
||||||
|
_currentPosF = e.Position;
|
||||||
|
|
||||||
|
if (int.TryParse(e.Id, out int tag))
|
||||||
|
{
|
||||||
|
numericUpDown1.Text = tag.ToString();
|
||||||
|
}
|
||||||
|
UpdateVisualAGV();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void button14_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//agv 정지
|
||||||
|
//비상정지 에러 플래그
|
||||||
|
AGV.SetAGV(DevAGV.eerror.Emergency, true);
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.agv_run, false);
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.agv_stop, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void button16_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.stop_by_front_detect, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button15_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.stop_by_front_detect, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button17_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//agv 정지
|
||||||
|
//비상정지 에러 플래그
|
||||||
|
AGV.SetAGV(DevAGV.eerror.Emergency, false);
|
||||||
|
AGV.SetAGV(DevAGV.eerror.line_out_error, false);
|
||||||
|
AGV.SetAGV(DevAGV.eerror.Overcurrent, false);
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.agv_run, false);
|
||||||
|
AGV.SetAGV(DevAGV.esystemflag1.agv_stop, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void Chk_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var chk = sender as CheckBox;
|
||||||
|
var idx = int.Parse(chk.Tag.ToString());
|
||||||
|
|
||||||
|
switch (chk.Parent.Tag.ToString().ToLower())
|
||||||
|
{
|
||||||
|
case "s0":
|
||||||
|
var v0 = (DevAGV.esystemflag0)idx;
|
||||||
|
AGV.SetAGV(v0, chk.Checked);
|
||||||
|
break;
|
||||||
|
case "s1":
|
||||||
|
var v1 = (DevAGV.esystemflag1)idx;
|
||||||
|
AGV.SetAGV(v1, chk.Checked);
|
||||||
|
break;
|
||||||
|
case "er":
|
||||||
|
var v2 = (DevAGV.eerror)idx;
|
||||||
|
AGV.SetAGV(v2, chk.Checked);
|
||||||
|
break;
|
||||||
|
case "sg":
|
||||||
|
var v3 = (DevAGV.esignal)idx;
|
||||||
|
AGV.SetAGV(v3, chk.Checked);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chk.BackColor = chk.Checked ? Color.Lime : SystemColors.Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button18_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (int.TryParse(numericUpDown1.Text, out int tagno))
|
||||||
|
{
|
||||||
|
AGV.SendTag(tagno.ToString());
|
||||||
|
numericUpDown1.SelectAll();
|
||||||
|
numericUpDown1.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void agvViewer1_MouseDown(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButtons.Left)
|
||||||
|
{
|
||||||
|
agvViewer1.StopbyMark = !agvViewer1.StopbyMark;
|
||||||
|
}
|
||||||
|
else if (e.Button == MouseButtons.Right)
|
||||||
|
{
|
||||||
|
MakeViewer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button6_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var target = (byte)nudIDAgv.Value;
|
||||||
|
var tagno = (uint)nudTagNo.Value;
|
||||||
|
this.XBE.SendGotoTag(target, tagno);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var target = (byte)nudIDAgv.Value;
|
||||||
|
var tagno = (uint)numericUpDown2.Value;
|
||||||
|
this.XBE.SendCurrentPos(target, tagno);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt16 Temp1, Temp2;
|
||||||
|
private void trbT1_Scroll(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//값에 /10해서 표시한다.
|
||||||
|
Temp1 = (UInt16)trbT1.Value;
|
||||||
|
label10.Text = $"{Temp1 / 10f}º";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyCode == Keys.Enter)
|
||||||
|
button18.PerformClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton3_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var file = @"C:\Data\Amkor\AGV4\route\NewMap.agvmap";
|
||||||
|
if(System.IO.File.Exists(file)==false)
|
||||||
|
{
|
||||||
|
var od = new OpenFileDialog();
|
||||||
|
od.Filter = "json|*.json";
|
||||||
|
if (od.ShowDialog() != DialogResult.OK) return;
|
||||||
|
file = od.FileName;
|
||||||
|
}
|
||||||
|
LoadMapFile(file);
|
||||||
|
}
|
||||||
|
private string _currentMapFilePath;
|
||||||
|
private void LoadMapFile(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = MapLoader.LoadMapFromFile(filePath);
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Map File Load : {filePath}");
|
||||||
|
|
||||||
|
// 시뮬레이터 캔버스에 맵 설정
|
||||||
|
this._agvCanvas.SetMapLoadResult(result);// = _mapNodes;
|
||||||
|
|
||||||
|
// 맵 설정 적용 (배경색, 그리드 표시)
|
||||||
|
if (result.Settings != null)
|
||||||
|
{
|
||||||
|
_agvCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
|
||||||
|
_agvCanvas.ShowGrid = result.Settings.ShowGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 맵에 맞춤
|
||||||
|
_agvCanvas.FitToNodes();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"맵 파일 로드 실패: {result.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"맵 파일 로드 실패: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton4_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Change rotation (180 degrees)
|
||||||
|
if (_visualAgv != null)
|
||||||
|
{
|
||||||
|
_visualAgv.Angle = (_visualAgv.Angle + 180) % 360;
|
||||||
|
UpdateVisualAGV();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton5_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Set AGV Position by RFID
|
||||||
|
string input = "";
|
||||||
|
using (Form form = new Form())
|
||||||
|
{
|
||||||
|
form.Text = "Set AGV Position";
|
||||||
|
form.Size = new Size(300, 150);
|
||||||
|
Label lbl = new Label() { Left = 20, Top = 20, Text = "Enter RFID (Node ID):", AutoSize = true };
|
||||||
|
TextBox txt = new TextBox() { Left = 20, Top = 45, Width = 240 };
|
||||||
|
Button btn = new Button() { Text = "OK", Left = 180, Width = 80, Top = 75, DialogResult = DialogResult.OK };
|
||||||
|
form.Controls.Add(lbl);
|
||||||
|
form.Controls.Add(txt);
|
||||||
|
form.Controls.Add(btn);
|
||||||
|
form.AcceptButton = btn;
|
||||||
|
form.StartPosition = FormStartPosition.CenterParent;
|
||||||
|
|
||||||
|
if (form.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
input = txt.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(input) && _agvCanvas.Nodes != null && _visualAgv != null)
|
||||||
|
{
|
||||||
|
var node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == input);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
_visualAgv.CurrentPosition = node.Position;
|
||||||
|
_currentPosF = node.Position;
|
||||||
|
numericUpDown1.Text = node.Id;
|
||||||
|
|
||||||
|
// Auto-orient: Prefer Right (0) or Up (270)
|
||||||
|
var neighbors = _agvCanvas.Nodes.Where(n => n != node &&
|
||||||
|
Math.Sqrt(Math.Pow(n.Position.X - node.Position.X, 2) + Math.Pow(n.Position.Y - node.Position.Y, 2)) < 150)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (neighbors.Any())
|
||||||
|
{
|
||||||
|
var rightNeighbor = neighbors.FirstOrDefault(n => n.Position.X > node.Position.X && Math.Abs(n.Position.Y - node.Position.Y) < 50);
|
||||||
|
if (rightNeighbor != null)
|
||||||
|
{
|
||||||
|
_visualAgv.Angle = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var upNeighbor = neighbors.FirstOrDefault(n => n.Position.Y < node.Position.Y && Math.Abs(n.Position.X - node.Position.X) < 50);
|
||||||
|
if (upNeighbor != null)
|
||||||
|
{
|
||||||
|
_visualAgv.Angle = 270;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var first = neighbors.First();
|
||||||
|
double angle = Math.Atan2(first.Position.Y - node.Position.Y, first.Position.X - node.Position.X) * 180 / Math.PI;
|
||||||
|
if (angle < 0) angle += 360;
|
||||||
|
_visualAgv.Angle = angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateVisualAGV();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Node '{input}' not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void trbT2_Scroll(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Temp2 = (UInt16)trbT2.Value;
|
||||||
|
label11.Text = $"{Temp2 / 10f}º";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
serAGV.Connect();
|
||||||
|
serBMS.Connect();
|
||||||
|
serCAL.Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
serAGV.Disconnect();
|
||||||
|
serBMS.Disconnect();
|
||||||
|
serCAL.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
205
AGVEmulator/fMain.resx
Normal file
205
AGVEmulator/fMain.resx
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
<?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="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>104, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
|
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>210, 17</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
3
Cs_HMI/.cursorrules
Normal file
3
Cs_HMI/.cursorrules
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# User Preferences
|
||||||
|
- **프로젝트 컴파일은 build.bat 를 사용하세요(윈도우 환경)
|
||||||
|
- **콘솔명령 실행시에는 항상 cmd /c 를 사용해서 실행하세요
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Express 15 for Windows Desktop
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 15.0.36324.19
|
VisualStudioVersion = 17.14.36804.6 d17.14
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sub", "Sub", "{C423C39A-44E7-4F09-B2F7-7943975FF948}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sub", "Sub", "{C423C39A-44E7-4F09-B2F7-7943975FF948}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVLogic\AG
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVLogic\AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVLogic\AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "SubProject\commutil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -166,6 +168,18 @@ Global
|
|||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -178,6 +192,7 @@ Global
|
|||||||
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
||||||
{B2C3D4E5-0000-0000-0000-000000000000} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
{B2C3D4E5-0000-0000-0000-000000000000} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
|
||||||
|
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}
|
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
|
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\MapEditor\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
@@ -62,7 +62,6 @@
|
|||||||
<Compile Include="Models\ImagePathEditor.cs" />
|
<Compile Include="Models\ImagePathEditor.cs" />
|
||||||
<Compile Include="Models\MapImage.cs" />
|
<Compile Include="Models\MapImage.cs" />
|
||||||
<Compile Include="Models\MapLabel.cs" />
|
<Compile Include="Models\MapLabel.cs" />
|
||||||
<Compile Include="Models\NodePropertyWrapper.cs" />
|
|
||||||
<Compile Include="Forms\MainForm.cs">
|
<Compile Include="Forms\MainForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ namespace AGVMapEditor.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ImageEditorForm : Form
|
public partial class ImageEditorForm : Form
|
||||||
{
|
{
|
||||||
private MapNode _targetNode;
|
private MapImage _targetNode;
|
||||||
|
|
||||||
public ImageEditorForm(MapNode imageNode = null)
|
public ImageEditorForm(MapImage imageNode = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_targetNode = imageNode;
|
_targetNode = imageNode;
|
||||||
@@ -48,7 +48,7 @@ namespace AGVMapEditor.Forms
|
|||||||
imageCanvas.BrushSize = trackBrush.Value;
|
imageCanvas.BrushSize = trackBrush.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadImageFromNode(MapNode node)
|
private void LoadImageFromNode(MapImage node)
|
||||||
{
|
{
|
||||||
if (node.LoadedImage != null)
|
if (node.LoadedImage != null)
|
||||||
{
|
{
|
||||||
@@ -160,7 +160,7 @@ namespace AGVMapEditor.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_targetNode != null && _targetNode.Type == NodeType.Image)
|
if (_targetNode != null)
|
||||||
{
|
{
|
||||||
// 표시 크기로 리사이즈된 이미지 가져오기
|
// 표시 크기로 리사이즈된 이미지 가져오기
|
||||||
var finalImage = imageCanvas.GetResizedImage();
|
var finalImage = imageCanvas.GetResizedImage();
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ namespace AGVMapEditor.Forms
|
|||||||
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.btnSave = new System.Windows.Forms.ToolStripButton();
|
this.btnSave = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btnSaveAs = new System.Windows.Forms.ToolStripButton();
|
this.btnSaveAs = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
|
this.toolStripButton1 = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.statusStrip1.SuspendLayout();
|
this.statusStrip1.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
||||||
this.splitContainer1.Panel1.SuspendLayout();
|
this.splitContainer1.Panel1.SuspendLayout();
|
||||||
@@ -351,7 +354,9 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnClose,
|
this.btnClose,
|
||||||
this.toolStripSeparator3,
|
this.toolStripSeparator3,
|
||||||
this.btnSave,
|
this.btnSave,
|
||||||
this.btnSaveAs});
|
this.btnSaveAs,
|
||||||
|
this.toolStripSeparator2,
|
||||||
|
this.toolStripButton1});
|
||||||
this.toolStrip2.Location = new System.Drawing.Point(0, 0);
|
this.toolStrip2.Location = new System.Drawing.Point(0, 0);
|
||||||
this.toolStrip2.Name = "toolStrip2";
|
this.toolStrip2.Name = "toolStrip2";
|
||||||
this.toolStrip2.Size = new System.Drawing.Size(1200, 25);
|
this.toolStrip2.Size = new System.Drawing.Size(1200, 25);
|
||||||
@@ -417,6 +422,28 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnSaveAs.ToolTipText = "다른 이름으로 저장";
|
this.btnSaveAs.ToolTipText = "다른 이름으로 저장";
|
||||||
this.btnSaveAs.Click += new System.EventHandler(this.btnSaveAs_Click);
|
this.btnSaveAs.Click += new System.EventHandler(this.btnSaveAs_Click);
|
||||||
//
|
//
|
||||||
|
// toolStripSeparator2
|
||||||
|
//
|
||||||
|
this.toolStripSeparator2.Name = "toolStripSeparator2";
|
||||||
|
this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
|
||||||
|
//
|
||||||
|
// toolStripButton1
|
||||||
|
//
|
||||||
|
this.toolStripButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem});
|
||||||
|
this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
|
||||||
|
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.toolStripButton1.Name = "toolStripButton1";
|
||||||
|
this.toolStripButton1.Size = new System.Drawing.Size(68, 22);
|
||||||
|
this.toolStripButton1.Text = "Debig";
|
||||||
|
//
|
||||||
|
// allTurnLeftRightCrossOnToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Name = "allTurnLeftRightCrossOnToolStripMenuItem";
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Size = new System.Drawing.Size(223, 22);
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Text = "All TurnLeft/Right/Cross On";
|
||||||
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Click += new System.EventHandler(this.allTurnLeftRightCrossOnToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
// MainForm
|
// MainForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
@@ -490,5 +517,8 @@ namespace AGVMapEditor.Forms
|
|||||||
private System.Windows.Forms.ToolStripButton btnAddImage;
|
private System.Windows.Forms.ToolStripButton btnAddImage;
|
||||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
||||||
private System.Windows.Forms.Panel panel1;
|
private System.Windows.Forms.Panel panel1;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
|
||||||
|
private System.Windows.Forms.ToolStripDropDownButton toolStripButton1;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem allTurnLeftRightCrossOnToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ using System.Windows.Forms;
|
|||||||
using AGVMapEditor.Models;
|
using AGVMapEditor.Models;
|
||||||
using AGVNavigationCore.Controls;
|
using AGVNavigationCore.Controls;
|
||||||
using AGVNavigationCore.Models;
|
using AGVNavigationCore.Models;
|
||||||
|
using MapImage = AGVNavigationCore.Models.MapImage;
|
||||||
|
using MapLabel = AGVNavigationCore.Models.MapLabel;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace AGVMapEditor.Forms
|
namespace AGVMapEditor.Forms
|
||||||
@@ -18,11 +20,11 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
|
|
||||||
private List<MapNode> _mapNodes;
|
// private List<MapNode> this._mapCanvas.Nodes;
|
||||||
private UnifiedAGVCanvas _mapCanvas;
|
private UnifiedAGVCanvas _mapCanvas;
|
||||||
|
|
||||||
// 현재 선택된 노드
|
// 현재 선택된 노드
|
||||||
private MapNode _selectedNode;
|
private NodeBase _selectedNode;
|
||||||
|
|
||||||
// 파일 경로
|
// 파일 경로
|
||||||
private string _currentMapFile = string.Empty;
|
private string _currentMapFile = string.Empty;
|
||||||
@@ -34,20 +36,20 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
public string FromNodeId { get; set; }
|
public string FromNodeId { get; set; }
|
||||||
public string FromNodeName { get; set; }
|
public string FromNodeName { get; set; }
|
||||||
public string FromRfidId { get; set; }
|
public ushort FromRfidId { get; set; }
|
||||||
public string ToNodeId { get; set; }
|
public string ToNodeId { get; set; }
|
||||||
public string ToNodeName { get; set; }
|
public string ToNodeName { get; set; }
|
||||||
public string ToRfidId { get; set; }
|
public ushort ToRfidId { get; set; }
|
||||||
public string ConnectionType { get; set; }
|
public string ConnectionType { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
||||||
string fromDisplay = !string.IsNullOrEmpty(FromRfidId)
|
string fromDisplay = FromRfidId > 0
|
||||||
? $"{FromRfidId}({FromNodeName})"
|
? $"{FromRfidId}({FromNodeName})"
|
||||||
: $"---({FromNodeId})";
|
: $"---({FromNodeId})";
|
||||||
|
|
||||||
string toDisplay = !string.IsNullOrEmpty(ToRfidId)
|
string toDisplay = ToRfidId > 0
|
||||||
? $"{ToRfidId}({ToNodeName})"
|
? $"{ToRfidId}({ToNodeName})"
|
||||||
: $"---({ToNodeId})";
|
: $"---({ToNodeId})";
|
||||||
|
|
||||||
@@ -98,15 +100,13 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
private void InitializeData()
|
private void InitializeData()
|
||||||
{
|
{
|
||||||
_mapNodes = new List<MapNode>();
|
// this._mapCanvas.Nodes = new List<MapNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeMapCanvas()
|
private void InitializeMapCanvas()
|
||||||
{
|
{
|
||||||
_mapCanvas = new UnifiedAGVCanvas();
|
_mapCanvas = new UnifiedAGVCanvas();
|
||||||
_mapCanvas.Dock = DockStyle.Fill;
|
_mapCanvas.Dock = DockStyle.Fill;
|
||||||
_mapCanvas.Nodes = _mapNodes;
|
|
||||||
// RfidMappings 제거 - MapNode에 통합됨
|
|
||||||
|
|
||||||
// 이벤트 연결
|
// 이벤트 연결
|
||||||
_mapCanvas.NodeAdded += OnNodeAdded;
|
_mapCanvas.NodeAdded += OnNodeAdded;
|
||||||
@@ -115,12 +115,14 @@ namespace AGVMapEditor.Forms
|
|||||||
_mapCanvas.NodeMoved += OnNodeMoved;
|
_mapCanvas.NodeMoved += OnNodeMoved;
|
||||||
_mapCanvas.NodeDeleted += OnNodeDeleted;
|
_mapCanvas.NodeDeleted += OnNodeDeleted;
|
||||||
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
||||||
_mapCanvas.ImageNodeDoubleClicked += OnImageNodeDoubleClicked;
|
_mapCanvas.ImageDoubleClicked += OnImageDoubleClicked;
|
||||||
_mapCanvas.MapChanged += OnMapChanged;
|
_mapCanvas.MapChanged += OnMapChanged;
|
||||||
|
|
||||||
// 스플리터 패널에 맵 캔버스 추가
|
// 스플리터 패널에 맵 캔버스 추가
|
||||||
panel1.Controls.Add(_mapCanvas);
|
panel1.Controls.Add(_mapCanvas);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
// 툴바 버튼 이벤트 연결
|
// 툴바 버튼 이벤트 연결
|
||||||
WireToolbarButtonEvents();
|
WireToolbarButtonEvents();
|
||||||
}
|
}
|
||||||
@@ -174,7 +176,7 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNodeAdded(object sender, MapNode node)
|
private void OnNodeAdded(object sender, NodeBase node)
|
||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
@@ -199,7 +201,7 @@ namespace AGVMapEditor.Forms
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
private void OnNodesSelected(object sender, List<MapNode> nodes)
|
private void OnNodesSelected(object sender, List<NodeBase> nodes)
|
||||||
{
|
{
|
||||||
// 다중 선택 시 처리
|
// 다중 선택 시 처리
|
||||||
if (nodes == null || nodes.Count == 0)
|
if (nodes == null || nodes.Count == 0)
|
||||||
@@ -221,20 +223,20 @@ namespace AGVMapEditor.Forms
|
|||||||
toolStripStatusLabel1.Text = $"{nodes.Count}개 노드 선택됨 - PropertyGrid에서 공통 속성 일괄 변경 가능";
|
toolStripStatusLabel1.Text = $"{nodes.Count}개 노드 선택됨 - PropertyGrid에서 공통 속성 일괄 변경 가능";
|
||||||
|
|
||||||
// 다중 선택 PropertyWrapper 표시
|
// 다중 선택 PropertyWrapper 표시
|
||||||
var multiWrapper = new MultiNodePropertyWrapper(nodes);
|
//var multiWrapper = new MultiNodePropertyWrapper(nodes);
|
||||||
_propertyGrid.SelectedObject = multiWrapper;
|
_propertyGrid.SelectedObjects = nodes.ToArray();// multiWrapper;
|
||||||
_propertyGrid.Focus();
|
_propertyGrid.Focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNodeMoved(object sender, MapNode node)
|
private void OnNodeMoved(object sender, NodeBase node)
|
||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
RefreshNodeList();
|
RefreshNodeList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNodeDeleted(object sender, MapNode node)
|
private void OnNodeDeleted(object sender, NodeBase node)
|
||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
@@ -258,12 +260,10 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateNodeProperties(); // 연결 정보 업데이트
|
UpdateNodeProperties(); // 연결 정보 업데이트
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnImageNodeDoubleClicked(object sender, MapNode node)
|
private void OnImageDoubleClicked(object sender, MapImage image)
|
||||||
{
|
{
|
||||||
// 이미지 노드 더블클릭 시 이미지 편집창 표시
|
// 이미지 노드 더블클릭 시 이미지 편집창 표시
|
||||||
if (node != null && node.Type == NodeType.Image)
|
using (var editor = new ImageEditorForm(image))
|
||||||
{
|
|
||||||
using (var editor = new ImageEditorForm(node))
|
|
||||||
{
|
{
|
||||||
if (editor.ShowDialog(this) == DialogResult.OK)
|
if (editor.ShowDialog(this) == DialogResult.OK)
|
||||||
{
|
{
|
||||||
@@ -274,7 +274,6 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMapChanged(object sender, EventArgs e)
|
private void OnMapChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -401,12 +400,11 @@ namespace AGVMapEditor.Forms
|
|||||||
private void AddNewNode()
|
private void AddNewNode()
|
||||||
{
|
{
|
||||||
var nodeId = GenerateNodeId();
|
var nodeId = GenerateNodeId();
|
||||||
var nodeName = $"노드{_mapNodes.Count + 1}";
|
var position = new Point(100 + this._mapCanvas.Nodes.Count * 50, 100 + this._mapCanvas.Nodes.Count * 50);
|
||||||
var position = new Point(100 + _mapNodes.Count * 50, 100 + _mapNodes.Count * 50);
|
|
||||||
|
|
||||||
var node = new MapNode(nodeId, nodeName, position, NodeType.Normal);
|
var node = new MapNode(nodeId, position, StationType.Normal);
|
||||||
|
|
||||||
_mapNodes.Add(node);
|
this._mapCanvas.Nodes.Add(node);
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
|
|
||||||
RefreshNodeList();
|
RefreshNodeList();
|
||||||
@@ -422,13 +420,14 @@ namespace AGVMapEditor.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = MessageBox.Show($"노드 '{_selectedNode.Name}'를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? 0;
|
||||||
|
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
||||||
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||||
|
|
||||||
if (result == DialogResult.Yes)
|
if (result == DialogResult.Yes)
|
||||||
{
|
{
|
||||||
// 노드 제거
|
// 노드 제거
|
||||||
_mapNodes.Remove(_selectedNode);
|
_mapCanvas.RemoveItem(_selectedNode);
|
||||||
_selectedNode = null;
|
_selectedNode = null;
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
|
|
||||||
@@ -441,15 +440,15 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
private void AddConnectionToSelectedNode()
|
private void AddConnectionToSelectedNode()
|
||||||
{
|
{
|
||||||
if (_selectedNode == null)
|
if (!(_selectedNode is MapNode selectedMapNode))
|
||||||
{
|
{
|
||||||
MessageBox.Show("연결을 추가할 노드를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show("연결을 추가할 노드(MapNode)를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 다른 노드들 중에서 선택
|
// 다른 노드들 중에서 선택
|
||||||
var availableNodes = _mapNodes.Where(n => n.NodeId != _selectedNode.NodeId &&
|
var availableNodes = this._mapCanvas.Nodes.Where(n => n.Id != selectedMapNode.Id &&
|
||||||
!_selectedNode.ConnectedNodes.Contains(n.NodeId)).ToList();
|
!selectedMapNode.ConnectedNodes.Contains(n.Id)).ToList();
|
||||||
|
|
||||||
if (availableNodes.Count == 0)
|
if (availableNodes.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -458,13 +457,13 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 간단한 선택 다이얼로그 (실제로는 별도 폼을 만들어야 함)
|
// 간단한 선택 다이얼로그 (실제로는 별도 폼을 만들어야 함)
|
||||||
var nodeNames = availableNodes.Select(n => $"{n.NodeId}: {n.Name}").ToArray();
|
var nodeNames = availableNodes.Select(n => $"{n.Id}: {n.RfidId}").ToArray();
|
||||||
var input = Microsoft.VisualBasic.Interaction.InputBox("연결할 노드를 선택하세요:", "노드 연결", nodeNames[0]);
|
var input = Microsoft.VisualBasic.Interaction.InputBox("연결할 노드를 선택하세요:", "노드 연결", nodeNames[0]);
|
||||||
|
|
||||||
var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.NodeId));
|
var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.Id));
|
||||||
if (targetNode != null)
|
if (targetNode != null)
|
||||||
{
|
{
|
||||||
_selectedNode.AddConnection(targetNode.NodeId);
|
selectedMapNode.AddConnection(targetNode.Id);
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
RefreshMapCanvas();
|
RefreshMapCanvas();
|
||||||
UpdateNodeProperties();
|
UpdateNodeProperties();
|
||||||
@@ -474,25 +473,25 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
private void RemoveConnectionFromSelectedNode()
|
private void RemoveConnectionFromSelectedNode()
|
||||||
{
|
{
|
||||||
if (_selectedNode == null || _selectedNode.ConnectedNodes.Count == 0)
|
if (!(_selectedNode is MapNode selectedMapNode) || selectedMapNode.ConnectedNodes.Count == 0)
|
||||||
{
|
{
|
||||||
MessageBox.Show("연결을 제거할 노드를 선택하거나 연결된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show("연결을 제거할 노드를 선택하거나 연결된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 연결된 노드들 중에서 선택
|
// 연결된 노드들 중에서 선택
|
||||||
var connectedNodeNames = _selectedNode.ConnectedNodes.Select(connectedNodeId =>
|
var connectedNodeNames = selectedMapNode.ConnectedNodes.Select(connectedNodeId =>
|
||||||
{
|
{
|
||||||
var node = _mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
var node = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectedNodeId);
|
||||||
return node != null ? $"{node.NodeId}: {node.Name}" : connectedNodeId;
|
return node != null ? $"{node.Id}: {node.RfidId}" : connectedNodeId;
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
var input = Microsoft.VisualBasic.Interaction.InputBox("제거할 연결을 선택하세요:", "연결 제거", connectedNodeNames[0]);
|
var input = Microsoft.VisualBasic.Interaction.InputBox("제거할 연결을 선택하세요:", "연결 제거", connectedNodeNames[0]);
|
||||||
|
|
||||||
var targetNodeId = input.Split(':')[0];
|
var targetNodeId = input.Split(':')[0];
|
||||||
if (_selectedNode.ConnectedNodes.Contains(targetNodeId))
|
if (selectedMapNode.ConnectedNodes.Contains(targetNodeId))
|
||||||
{
|
{
|
||||||
_selectedNode.RemoveConnection(targetNodeId);
|
selectedMapNode.RemoveConnection(targetNodeId);
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
RefreshMapCanvas();
|
RefreshMapCanvas();
|
||||||
UpdateNodeProperties();
|
UpdateNodeProperties();
|
||||||
@@ -509,7 +508,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
nodeId = $"N{counter:D3}";
|
nodeId = $"N{counter:D3}";
|
||||||
counter++;
|
counter++;
|
||||||
} while (_mapNodes.Any(n => n.NodeId == nodeId));
|
} while (this._mapCanvas.Nodes.Any(n => n.Id == nodeId));
|
||||||
|
|
||||||
return nodeId;
|
return nodeId;
|
||||||
}
|
}
|
||||||
@@ -520,7 +519,7 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
private void NewMap()
|
private void NewMap()
|
||||||
{
|
{
|
||||||
_mapNodes.Clear();
|
this._mapCanvas.Nodes.Clear();
|
||||||
_selectedNode = null;
|
_selectedNode = null;
|
||||||
_currentMapFile = string.Empty;
|
_currentMapFile = string.Empty;
|
||||||
_hasChanges = false;
|
_hasChanges = false;
|
||||||
@@ -533,7 +532,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
if (CheckSaveChanges())
|
if (CheckSaveChanges())
|
||||||
{
|
{
|
||||||
_mapNodes.Clear();
|
this._mapCanvas.Nodes.Clear();
|
||||||
_selectedNode = null;
|
_selectedNode = null;
|
||||||
_currentMapFile = string.Empty;
|
_currentMapFile = string.Empty;
|
||||||
_hasChanges = false;
|
_hasChanges = false;
|
||||||
@@ -547,8 +546,8 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
var openFileDialog = new OpenFileDialog
|
var openFileDialog = new OpenFileDialog
|
||||||
{
|
{
|
||||||
Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*",
|
Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*",
|
||||||
DefaultExt = "agvmap",
|
DefaultExt = "json",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -596,9 +595,9 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
var saveFileDialog = new SaveFileDialog
|
var saveFileDialog = new SaveFileDialog
|
||||||
{
|
{
|
||||||
Filter = "AGV Map Files (*.agvmap)|*.agvmap",
|
Filter = "AGV Map Files (*.json)|*.json",
|
||||||
DefaultExt = "agvmap",
|
DefaultExt = "json",
|
||||||
FileName = "NewMap.agvmap"
|
FileName = "NewMap.json"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||||
@@ -624,18 +623,8 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
_mapNodes = result.Nodes;
|
|
||||||
|
|
||||||
// 맵 캔버스에 데이터 설정
|
// 맵 캔버스에 데이터 설정
|
||||||
_mapCanvas.Nodes = _mapNodes;
|
_mapCanvas.SetMapLoadResult(result);
|
||||||
// RfidMappings 제거됨 - MapNode에 통합
|
|
||||||
|
|
||||||
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
|
|
||||||
if (result.Settings != null)
|
|
||||||
{
|
|
||||||
_mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
|
|
||||||
_mapCanvas.ShowGrid = result.Settings.ShowGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 현재 파일 경로 업데이트
|
// 현재 파일 경로 업데이트
|
||||||
_currentMapFile = filePath;
|
_currentMapFile = filePath;
|
||||||
@@ -648,8 +637,8 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateNodeList();
|
UpdateNodeList();
|
||||||
RefreshNodeConnectionList();
|
RefreshNodeConnectionList();
|
||||||
|
|
||||||
// 맵 로드 후 자동으로 맵에 맞춤
|
|
||||||
_mapCanvas.FitToNodes();
|
|
||||||
|
|
||||||
UpdateStatusBar($"맵 파일을 성공적으로 로드했습니다: {Path.GetFileName(filePath)}");
|
UpdateStatusBar($"맵 파일을 성공적으로 로드했습니다: {Path.GetFileName(filePath)}");
|
||||||
}
|
}
|
||||||
@@ -693,7 +682,11 @@ namespace AGVMapEditor.Forms
|
|||||||
ShowGrid = _mapCanvas.ShowGrid
|
ShowGrid = _mapCanvas.ShowGrid
|
||||||
};
|
};
|
||||||
|
|
||||||
if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings))
|
if (MapLoader.SaveMapToFile(filePath,
|
||||||
|
_mapCanvas.Nodes, _mapCanvas.Labels,
|
||||||
|
_mapCanvas.Images, _mapCanvas.Marks,
|
||||||
|
_mapCanvas.Magnets,
|
||||||
|
settings))
|
||||||
{
|
{
|
||||||
// 현재 파일 경로 업데이트
|
// 현재 파일 경로 업데이트
|
||||||
_currentMapFile = filePath;
|
_currentMapFile = filePath;
|
||||||
@@ -718,7 +711,7 @@ namespace AGVMapEditor.Forms
|
|||||||
private void UpdateRfidMappings()
|
private void UpdateRfidMappings()
|
||||||
{
|
{
|
||||||
// RFID 자동 할당 제거 - 사용자가 직접 입력한 값 유지
|
// RFID 자동 할당 제거 - 사용자가 직접 입력한 값 유지
|
||||||
// MapLoader.AssignAutoRfidIds(_mapNodes);
|
// MapLoader.AssignAutoRfidIds(this._mapCanvas.Nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckSaveChanges()
|
private bool CheckSaveChanges()
|
||||||
@@ -780,9 +773,9 @@ namespace AGVMapEditor.Forms
|
|||||||
private void RefreshNodeList()
|
private void RefreshNodeList()
|
||||||
{
|
{
|
||||||
listBoxNodes.DataSource = null;
|
listBoxNodes.DataSource = null;
|
||||||
listBoxNodes.DataSource = _mapNodes;
|
listBoxNodes.DataSource = this._mapCanvas.Nodes;
|
||||||
listBoxNodes.DisplayMember = "DisplayText";
|
listBoxNodes.DisplayMember = "DisplayText";
|
||||||
listBoxNodes.ValueMember = "NodeId";
|
listBoxNodes.ValueMember = "Id";
|
||||||
|
|
||||||
// 노드 목록 클릭 이벤트 연결
|
// 노드 목록 클릭 이벤트 연결
|
||||||
listBoxNodes.SelectedIndexChanged -= ListBoxNodes_SelectedIndexChanged;
|
listBoxNodes.SelectedIndexChanged -= ListBoxNodes_SelectedIndexChanged;
|
||||||
@@ -812,9 +805,9 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
e.DrawBackground();
|
e.DrawBackground();
|
||||||
|
|
||||||
if (e.Index >= 0 && e.Index < _mapNodes.Count)
|
if (e.Index >= 0 && e.Index < this._mapCanvas.Items.Count)
|
||||||
{
|
{
|
||||||
var node = _mapNodes[e.Index];
|
var node = this._mapCanvas.Items[e.Index];
|
||||||
|
|
||||||
// 노드 타입에 따른 색상 설정
|
// 노드 타입에 따른 색상 설정
|
||||||
Color foreColor = Color.Black;
|
Color foreColor = Color.Black;
|
||||||
@@ -827,23 +820,30 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
backColor = Color.White;
|
||||||
switch (node.Type)
|
switch (node.Type)
|
||||||
{
|
{
|
||||||
case NodeType.Normal:
|
case NodeType.Normal:
|
||||||
foreColor = Color.Black;
|
|
||||||
backColor = Color.White;
|
var item = node as MapNode;
|
||||||
break;
|
if (item.StationType == StationType.Normal)
|
||||||
case NodeType.Rotation:
|
foreColor = Color.DimGray;
|
||||||
foreColor = Color.DarkOrange;
|
else if (item.StationType == StationType.Charger)
|
||||||
backColor = Color.LightYellow;
|
foreColor = Color.Red;
|
||||||
break;
|
else
|
||||||
case NodeType.Docking:
|
|
||||||
foreColor = Color.DarkGreen;
|
foreColor = Color.DarkGreen;
|
||||||
backColor = Color.LightGreen;
|
|
||||||
break;
|
break;
|
||||||
case NodeType.Charging:
|
|
||||||
|
case NodeType.Label:
|
||||||
|
case NodeType.Mark:
|
||||||
|
case NodeType.Image:
|
||||||
|
foreColor = Color.DarkBlue;
|
||||||
|
break;
|
||||||
|
case NodeType.Magnet:
|
||||||
|
foreColor = Color.DarkMagenta;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
foreColor = Color.DarkRed;
|
foreColor = Color.DarkRed;
|
||||||
backColor = Color.LightPink;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -855,17 +855,21 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 텍스트 그리기 (노드ID - 노드명 - RFID 순서)
|
// 텍스트 그리기 (노드ID - 노드명 - RFID 순서)
|
||||||
var displayText = node.NodeId;
|
string displayText;
|
||||||
|
if (node.Type == NodeType.Normal)
|
||||||
if (!string.IsNullOrEmpty(node.Name))
|
|
||||||
{
|
{
|
||||||
displayText += $" - {node.Name}";
|
var item = node as MapNode;
|
||||||
|
if (item.HasRfid())
|
||||||
|
displayText = $"[{node.Id}-{item.RfidId}] {item.StationType}";
|
||||||
|
else
|
||||||
|
displayText = $"[{node.Id}] {item.StationType}";
|
||||||
}
|
}
|
||||||
|
else if (node.Type == NodeType.Label)
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
|
||||||
{
|
{
|
||||||
displayText += $" - [{node.RfidId}]";
|
var item = node as MapLabel;
|
||||||
|
displayText = $"{item.Type.ToString().ToUpper()} - {item.Text}";
|
||||||
}
|
}
|
||||||
|
else displayText = $"{node.Type.ToString().ToUpper()}";
|
||||||
|
|
||||||
using (var brush = new SolidBrush(foreColor))
|
using (var brush = new SolidBrush(foreColor))
|
||||||
{
|
{
|
||||||
@@ -882,31 +886,31 @@ namespace AGVMapEditor.Forms
|
|||||||
var processedPairs = new HashSet<string>();
|
var processedPairs = new HashSet<string>();
|
||||||
|
|
||||||
// 모든 노드의 연결 정보를 수집 (중복 방지)
|
// 모든 노드의 연결 정보를 수집 (중복 방지)
|
||||||
foreach (var fromNode in _mapNodes)
|
foreach (var fromNode in this._mapCanvas.Nodes)
|
||||||
{
|
{
|
||||||
foreach (var toNodeId in fromNode.ConnectedNodes)
|
foreach (var toNodeId in fromNode.ConnectedNodes)
|
||||||
{
|
{
|
||||||
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == toNodeId);
|
var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == toNodeId);
|
||||||
if (toNode != null)
|
if (toNode != null)
|
||||||
{
|
{
|
||||||
// 중복 체크 (단일 연결만 표시)
|
// 중복 체크 (단일 연결만 표시)
|
||||||
string pairKey1 = $"{fromNode.NodeId}-{toNode.NodeId}";
|
string pairKey1 = $"{fromNode.Id}-{toNode.Id}";
|
||||||
string pairKey2 = $"{toNode.NodeId}-{fromNode.NodeId}";
|
string pairKey2 = $"{toNode.Id}-{fromNode.Id}";
|
||||||
|
|
||||||
if (!processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
|
if (!processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
|
||||||
{
|
{
|
||||||
// 사전 순으로 정렬하여 일관성 있게 표시
|
// 사전 순으로 정렬하여 일관성 있게 표시
|
||||||
var (firstNode, secondNode) = string.Compare(fromNode.NodeId, toNode.NodeId) < 0
|
var (firstNode, secondNode) = string.Compare(fromNode.Id, toNode.Id) < 0
|
||||||
? (fromNode, toNode)
|
? (fromNode, toNode)
|
||||||
: (toNode, fromNode);
|
: (toNode, fromNode);
|
||||||
|
|
||||||
connections.Add(new NodeConnectionInfo
|
connections.Add(new NodeConnectionInfo
|
||||||
{
|
{
|
||||||
FromNodeId = firstNode.NodeId,
|
FromNodeId = firstNode.Id,
|
||||||
FromNodeName = firstNode.Name,
|
FromNodeName = "",
|
||||||
FromRfidId = firstNode.RfidId,
|
FromRfidId = firstNode.RfidId,
|
||||||
ToNodeId = secondNode.NodeId,
|
ToNodeId = secondNode.Id,
|
||||||
ToNodeName = secondNode.Name,
|
ToNodeName = "",
|
||||||
ToRfidId = secondNode.RfidId,
|
ToRfidId = secondNode.RfidId,
|
||||||
ConnectionType = "양방향" // 모든 연결이 양방향
|
ConnectionType = "양방향" // 모든 연결이 양방향
|
||||||
});
|
});
|
||||||
@@ -941,7 +945,7 @@ namespace AGVMapEditor.Forms
|
|||||||
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
|
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
|
||||||
|
|
||||||
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
|
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
|
||||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId);
|
var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
||||||
if (fromNode != null)
|
if (fromNode != null)
|
||||||
{
|
{
|
||||||
_selectedNode = fromNode;
|
_selectedNode = fromNode;
|
||||||
@@ -975,9 +979,7 @@ namespace AGVMapEditor.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 노드 래퍼 객체 생성 (타입에 따라 다른 래퍼 사용)
|
_propertyGrid.SelectedObject = _selectedNode;
|
||||||
var nodeWrapper = NodePropertyWrapperFactory.CreateWrapper(_selectedNode, _mapNodes);
|
|
||||||
_propertyGrid.SelectedObject = nodeWrapper;
|
|
||||||
_propertyGrid.Focus();
|
_propertyGrid.Focus();
|
||||||
|
|
||||||
// 이미지 노드인 경우 편집 버튼 활성화
|
// 이미지 노드인 경우 편집 버튼 활성화
|
||||||
@@ -989,8 +991,7 @@ namespace AGVMapEditor.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void ShowCanvasProperties()
|
private void ShowCanvasProperties()
|
||||||
{
|
{
|
||||||
var canvasWrapper = new CanvasPropertyWrapper(_mapCanvas);
|
_propertyGrid.SelectedObject = _mapCanvas;
|
||||||
_propertyGrid.SelectedObject = canvasWrapper;
|
|
||||||
DisableImageEditButton();
|
DisableImageEditButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1006,7 +1007,7 @@ namespace AGVMapEditor.Forms
|
|||||||
private void UpdateImageEditButton()
|
private void UpdateImageEditButton()
|
||||||
{
|
{
|
||||||
// ToolStripButton으로 변경됨
|
// ToolStripButton으로 변경됨
|
||||||
btnEditImage.Enabled = (_selectedNode != null && _selectedNode.Type == NodeType.Image);
|
btnEditImage.Enabled = (_mapCanvas.SelectedImage != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1023,9 +1024,10 @@ namespace AGVMapEditor.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void BtnToolbarEditImage_Click(object sender, EventArgs e)
|
private void BtnToolbarEditImage_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedNode != null && _selectedNode.Type == NodeType.Image)
|
var selectedImage = _mapCanvas.SelectedImage;
|
||||||
|
if (selectedImage != null)
|
||||||
{
|
{
|
||||||
using (var editor = new ImageEditorForm(_selectedNode))
|
using (var editor = new ImageEditorForm(selectedImage))
|
||||||
{
|
{
|
||||||
if (editor.ShowDialog(this) == DialogResult.OK)
|
if (editor.ShowDialog(this) == DialogResult.OK)
|
||||||
{
|
{
|
||||||
@@ -1063,7 +1065,7 @@ namespace AGVMapEditor.Forms
|
|||||||
if (listBoxNodes != null)
|
if (listBoxNodes != null)
|
||||||
{
|
{
|
||||||
listBoxNodes.DataSource = null;
|
listBoxNodes.DataSource = null;
|
||||||
listBoxNodes.DataSource = _mapNodes;
|
listBoxNodes.DataSource = this._mapCanvas.Items;
|
||||||
listBoxNodes.DisplayMember = "DisplayText";
|
listBoxNodes.DisplayMember = "DisplayText";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1105,8 +1107,8 @@ namespace AGVMapEditor.Forms
|
|||||||
// RFID 값 변경시 중복 검사
|
// RFID 값 변경시 중복 검사
|
||||||
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
||||||
{
|
{
|
||||||
string newRfidValue = e.ChangedItem.Value?.ToString();
|
var newRfidValue = ushort.Parse(e.ChangedItem.Value?.ToString());
|
||||||
if (!string.IsNullOrEmpty(newRfidValue) && CheckRfidDuplicate(newRfidValue))
|
if (newRfidValue != 0 && CheckRfidDuplicate(newRfidValue))
|
||||||
{
|
{
|
||||||
// 중복된 RFID 값 발견
|
// 중복된 RFID 값 발견
|
||||||
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
|
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
|
||||||
@@ -1124,12 +1126,14 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
|
||||||
// 🔥 다중 선택 여부 확인 및 선택된 노드들 저장
|
// 🔥 다중 선택 여부 확인 및 선택된 노드들 저장
|
||||||
bool isMultiSelect = _propertyGrid.SelectedObject is MultiNodePropertyWrapper;
|
bool isMultiSelect = _propertyGrid.SelectedObjects is MapNode[]; // _propertyGrid.SelectedObject is MapNode[];
|
||||||
List<MapNode> selectedNodes = null;
|
|
||||||
|
//var a = _propertyGrid.SelectedObject;
|
||||||
|
List<NodeBase> selectedNodes = null;
|
||||||
if (isMultiSelect)
|
if (isMultiSelect)
|
||||||
{
|
{
|
||||||
// 캔버스에서 현재 선택된 노드들 가져오기
|
// 캔버스에서 현재 선택된 노드들 가져오기
|
||||||
selectedNodes = new List<MapNode>(_mapCanvas.SelectedNodes);
|
selectedNodes = new List<NodeBase>(_mapCanvas.SelectedNodes);
|
||||||
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}");
|
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1146,18 +1150,18 @@ namespace AGVMapEditor.Forms
|
|||||||
// 🔥 다중 선택인 경우 MultiNodePropertyWrapper를 다시 생성하여 바인딩
|
// 🔥 다중 선택인 경우 MultiNodePropertyWrapper를 다시 생성하여 바인딩
|
||||||
if (isMultiSelect && selectedNodes != null && selectedNodes.Count > 0)
|
if (isMultiSelect && selectedNodes != null && selectedNodes.Count > 0)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
|
// System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
|
||||||
var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
|
//var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
|
||||||
_propertyGrid.SelectedObject = multiWrapper;
|
_propertyGrid.SelectedObjects = selectedNodes.ToArray();// multiWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PropertyGrid 새로고침
|
// PropertyGrid 새로고침
|
||||||
_propertyGrid.Refresh();
|
_propertyGrid.Refresh();
|
||||||
|
|
||||||
// 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지)
|
// 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지)
|
||||||
if (!isMultiSelect && currentSelectedNode != null)
|
if (!isMultiSelect && currentSelectedNode is MapNode mapNode)
|
||||||
{
|
{
|
||||||
var nodeIndex = _mapNodes.IndexOf(currentSelectedNode);
|
var nodeIndex = this._mapCanvas.Nodes.IndexOf(mapNode);
|
||||||
if (nodeIndex >= 0)
|
if (nodeIndex >= 0)
|
||||||
{
|
{
|
||||||
listBoxNodes.SelectedIndex = nodeIndex;
|
listBoxNodes.SelectedIndex = nodeIndex;
|
||||||
@@ -1170,38 +1174,24 @@ namespace AGVMapEditor.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rfidValue">검사할 RFID 값</param>
|
/// <param name="rfidValue">검사할 RFID 값</param>
|
||||||
/// <returns>중복되면 true, 아니면 false</returns>
|
/// <returns>중복되면 true, 아니면 false</returns>
|
||||||
private bool CheckRfidDuplicate(string rfidValue)
|
private bool CheckRfidDuplicate(ushort rfidValue)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(rfidValue) || _mapNodes == null)
|
if (rfidValue == 0 || this._mapCanvas.Nodes == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 현재 편집 중인 노드 제외하고 중복 검사
|
// 현재 편집 중인 노드 제외하고 중복 검사
|
||||||
string currentNodeId = null;
|
string currentNodeId = null;
|
||||||
var selectedObject = _propertyGrid.SelectedObject;
|
var selectedObject = _propertyGrid.SelectedObject;
|
||||||
|
|
||||||
// 다양한 PropertyWrapper 타입 처리
|
|
||||||
if (selectedObject is NodePropertyWrapper nodeWrapper)
|
|
||||||
{
|
|
||||||
currentNodeId = nodeWrapper.WrappedNode?.NodeId;
|
|
||||||
}
|
|
||||||
else if (selectedObject is LabelNodePropertyWrapper labelWrapper)
|
|
||||||
{
|
|
||||||
currentNodeId = labelWrapper.WrappedNode?.NodeId;
|
|
||||||
}
|
|
||||||
else if (selectedObject is ImageNodePropertyWrapper imageWrapper)
|
|
||||||
{
|
|
||||||
currentNodeId = imageWrapper.WrappedNode?.NodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
int duplicateCount = 0;
|
int duplicateCount = 0;
|
||||||
foreach (var node in _mapNodes)
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
{
|
{
|
||||||
// 현재 편집 중인 노드는 제외
|
// 현재 편집 중인 노드는 제외
|
||||||
if (node.NodeId == currentNodeId)
|
if (node.Id == currentNodeId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 같은 RFID 값을 가진 노드가 있는지 확인
|
// 같은 RFID 값을 가진 노드가 있는지 확인
|
||||||
if (!string.IsNullOrEmpty(node.RfidId) && node.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase))
|
if (node.RfidId != 0 && node.RfidId == rfidValue)
|
||||||
{
|
{
|
||||||
duplicateCount++;
|
duplicateCount++;
|
||||||
break; // 하나라도 발견되면 중복
|
break; // 하나라도 발견되면 중복
|
||||||
@@ -1236,23 +1226,23 @@ namespace AGVMapEditor.Forms
|
|||||||
if (result == DialogResult.Yes)
|
if (result == DialogResult.Yes)
|
||||||
{
|
{
|
||||||
// 단일 연결 삭제
|
// 단일 연결 삭제
|
||||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId);
|
var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
||||||
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.ToNodeId);
|
var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.ToNodeId);
|
||||||
|
|
||||||
if (fromNode != null && toNode != null)
|
if (fromNode != null && toNode != null)
|
||||||
{
|
{
|
||||||
// 양방향 연결 삭제 (양쪽 방향 모두 제거)
|
// 양방향 연결 삭제 (양쪽 방향 모두 제거)
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
|
|
||||||
if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
|
if (fromNode.ConnectedNodes.Contains(toNode.Id))
|
||||||
{
|
{
|
||||||
fromNode.RemoveConnection(toNode.NodeId);
|
fromNode.RemoveConnection(toNode.Id);
|
||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
|
if (toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||||
{
|
{
|
||||||
toNode.RemoveConnection(fromNode.NodeId);
|
toNode.RemoveConnection(fromNode.Id);
|
||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1280,95 +1270,46 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void allTurnLeftRightCrossOnToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
#region Multi-Node Selection
|
|
||||||
|
|
||||||
private void ShowMultiNodeContextMenu(List<MapNode> nodes)
|
|
||||||
{
|
{
|
||||||
// 다중 선택 시 간단한 다이얼로그 표시
|
//모든노드의 trun left/right/ cross 속성을 true로 변경합니다
|
||||||
|
if (this._mapCanvas.Nodes == null || this._mapCanvas.Nodes.Count == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show("맵에 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var result = MessageBox.Show(
|
var result = MessageBox.Show(
|
||||||
$"{nodes.Count}개의 노드가 선택되었습니다.\n\n" +
|
$"모든 노드({this._mapCanvas.Nodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" +
|
||||||
"일괄 속성 변경을 하시겠습니까?\n\n" +
|
"• CanTurnLeft = true\n" +
|
||||||
"예: 글자색 변경\n" +
|
"• CanTurnRight = true\n" +
|
||||||
"아니오: 배경색 변경\n" +
|
"• DisableCross = false",
|
||||||
"취소: 닫기",
|
"일괄 속성 변경",
|
||||||
"다중 노드 속성 변경",
|
MessageBoxButtons.YesNo,
|
||||||
MessageBoxButtons.YesNoCancel,
|
|
||||||
MessageBoxIcon.Question);
|
MessageBoxIcon.Question);
|
||||||
|
|
||||||
if (result == DialogResult.Yes)
|
if (result == DialogResult.Yes)
|
||||||
{
|
{
|
||||||
BatchChangeForeColor();
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
}
|
|
||||||
else if (result == DialogResult.No)
|
|
||||||
{
|
{
|
||||||
BatchChangeBackColor();
|
node.CanTurnLeft = true;
|
||||||
}
|
node.CanTurnRight = true;
|
||||||
}
|
node.DisableCross = false;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 선택된 노드들의 글자색 일괄 변경
|
|
||||||
/// </summary>
|
|
||||||
public void BatchChangeForeColor()
|
|
||||||
{
|
|
||||||
var selectedNodes = _mapCanvas.SelectedNodes;
|
|
||||||
if (selectedNodes == null || selectedNodes.Count == 0)
|
|
||||||
{
|
|
||||||
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var colorDialog = new ColorDialog())
|
|
||||||
{
|
|
||||||
colorDialog.Color = selectedNodes[0].ForeColor;
|
|
||||||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
|
||||||
{
|
|
||||||
foreach (var node in selectedNodes)
|
|
||||||
{
|
|
||||||
node.ForeColor = colorDialog.Color;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
node.ModifiedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
RefreshMapCanvas();
|
RefreshMapCanvas();
|
||||||
MessageBox.Show($"{selectedNodes.Count}개 노드의 글자색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
MessageBox.Show(
|
||||||
/// 선택된 노드들의 배경색 일괄 변경
|
$"{this._mapCanvas.Nodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.",
|
||||||
/// </summary>
|
"완료",
|
||||||
public void BatchChangeBackColor()
|
MessageBoxButtons.OK,
|
||||||
{
|
MessageBoxIcon.Information);
|
||||||
var selectedNodes = _mapCanvas.SelectedNodes;
|
|
||||||
if (selectedNodes == null || selectedNodes.Count == 0)
|
|
||||||
{
|
|
||||||
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var colorDialog = new ColorDialog())
|
UpdateStatusBar($"모든 노드의 회전/교차 속성 활성화 완료");
|
||||||
{
|
|
||||||
colorDialog.Color = selectedNodes[0].DisplayColor;
|
|
||||||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
|
||||||
{
|
|
||||||
foreach (var node in selectedNodes)
|
|
||||||
{
|
|
||||||
node.DisplayColor = colorDialog.Color;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasChanges = true;
|
|
||||||
UpdateTitle();
|
|
||||||
RefreshMapCanvas();
|
|
||||||
MessageBox.Show($"{selectedNodes.Count}개 노드의 배경색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -120,22 +120,40 @@
|
|||||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>132, 17</value>
|
<value>132, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>249, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
|
<data name="btNodeRemove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
<metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>462, 17</value>
|
<value>462, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
|
||||||
<data name="btnSelect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnSelect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBNiBJhHMfx/ykKiohQ18tEhygyWGQRJwh1
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS
|
||||||
fcHAt7qJ3TOj6FJ0akP06G2RPQd1MNhKcxvCxDc2UHbXSCoWNu8dWp3ReTks/moGVnafjW33c/rPM8/3
|
pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQcxdq3d3s61Dy0w002KnU9nP67+x8
|
||||||
4eEhOoLJZ/6UvDp7l10/lNKa4+TW3AN9Hq9eC8kNW1ef1QZ3UapzRztMbs1+lVs2Qa5fLqsbtzBucE9G
|
h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb
|
||||||
NetwVD2fYPf+07h5ZUH9koD24yG0rWdQ1kMQK2eVXzXTaXbv1Lh9dUZu2apy4xLUjSi0nxk0X0bRLd7B
|
dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj
|
||||||
Vu0elM48xE8nIX6kJbbdZ1znFpTubTRfxaBpGiaTCdbfJaB+v4+BQN+2y3SBbaak5oxJqlqEcDiMTqcD
|
cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50
|
||||||
VVXR6/VQexGCtvkYYplG4gfi2c6gVKzcqHKuGIvFkEwmkcvlUCgUUF6ah7b5FIMV2hFXaG1YoudsOxWN
|
XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx
|
||||||
Ro2Y53l4vV5EIhHIayFIVSsk4Qy2S8SxzdRu7HQ64fF4YDab9SsvDt7Tjlii62KRusM3lGI7QzweRzab
|
+DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+
|
||||||
NWK32w2TyQR9XSrRDfEtCfqM13Ti9zLdZFsKBoPIZDIH4l3DZQrs/d7H5/MhnU7D4XDA5XIdiA+VSqXQ
|
P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD
|
||||||
7/dht9sRCASOF+v0A9rtNvL5vPFg7P//8vv9j/RrWyyW48d//QFpDAsmbMS+zgAAAABJRU5ErkJggg==
|
wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29OYwsb4/6fnQAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnMove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnMove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
@@ -156,75 +174,75 @@
|
|||||||
<data name="btnAddLabel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnAddLabel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHMSURBVDhPnZJfa9pQGMb7JXa7sW9RCP1ku1lXRi+3luJa
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5ddS3Et
|
||||||
dteOjYi5CJhoBIPGLYkExb8zic5molPnoAO3k80ozzhHTopzq2wPhHDe8z6/5yV599rtNlqtFhqNBur1
|
u2vHRsRcBEw0iEHiYpSg+Hcm0blUtOocdOCWbEZ5xnnlTXFule2BEN7znuf3HJKz12630Wq10Gg0UK/X
|
||||||
Omq1GqrVKiqVCn3f29ulZrOJ1Wq19QyHQwYxDONuCE2mhsFgwJKDIIghtOY4zt0QOjJtpmn0XC6XK/P5
|
UavVUK1WUalU6P1gb5eazSZWq9XWMxwOGUTX9fshlEyGwWDAkj3PiyBUsyzrfgiNTM2URudyuVyZz+cI
|
||||||
HFEUsToFWpb1dwhN5Y22bf80TXMQhiE+f/qOd9qU3fX7fZRKpT9DaDIfeblcgpq/TEO8OPJx8sjD2+wa
|
w5DVCWia5t8hlMobDcP4aRjGIAgCfL75DkOdsrt+v49CofBnCCXzkZfLJcj8ZRrg1aGLk6cO3mfWkF6v
|
||||||
0uv1UCgUoKrqJsRxHDKbzVjTYrFgyYknPi6f9fH6LMD5UTeG+L6PfD6/CbFt+8CyLDKdrpvc+g1Lvjq5
|
h3w+D0VRNiGWZfmz2Yw1LRYLlhx/7uLyuI+3Zx7OD7sRxHVd5HK5TUipVHpimqY/na6b7PotS746+QTh
|
||||||
hnj+cQvieR40TduEmKYpFItFMplMWFO39RWJQw+vTm8hiUMfhrq+73Q6UBQFoijeQgzDEHRdJ+PxeAvy
|
/HoL4jgOVFXdhBSLxZimaf5kMmFN3dZXxA8cvDm9g8QPXOjK+r7T6UCWZQiCcAfRdT2WzWb98Xi8BXkX
|
||||||
JhHg5XEPF8fvEUVLNkUmk0EqlXoQA6h0XRdyuRwZjUYx5Oyxi4unXVyddvFtHsZmURQfbpi5NE0Tstks
|
9/D6qIeLow8IwyWbIp1OI5lMPooAJE3TYplMxh+NRhHk7JmNixddXJ128W0eRGZBEB5vmLlUVWUQ2kT2
|
||||||
oZvIPlzzBpfPP+BHGO02cymKIqTTaUI3kf9ialZVdbeZS5blfVmWCV0w13X/zcwlSdK+JEnkv8xcFJJM
|
4Zq3uHz5ET+CcLeZS5blWCqV8mkT+S8ms6Iou81ckiTtS5Lk04LZtv1vZi5RFPdFUfT/y8xFkEQi8fD3
|
||||||
Ju//Xuf6Be3RFseUK14pAAAAAElFTkSuQmCC
|
Otcvn84Wo7k6b1AAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG8SURBVDhPnZLditpQFIXnJXrb0rcYCH2wXhSZwly2U0rp
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaXM
|
||||||
O0TMRUp+BTMaIYkERY3BH0TEalW8KEV6gp4cVjlnOBms7Ui7IITsvde3NmRfpWmKwWCAfr+PXq+HbreL
|
O0TMRUp+ETM2EqMERY3BH0TE0ap4UQaZE/TksIZzhpPBOjPSLgghe+/1rQ3ZZ3Eco9frodvtotPpoN1u
|
||||||
TqeDdrvN38+uLilJEjDGzp7lcikgvu8/DeHJ3LBYLETyfD4vILwWx/HTEL4yH+Zp/LvVarX3+z0opaLO
|
o9Vqodls8vebs1OKogiMsaNnPp8LiOd5r0N4MjfMZjORPJ1OMwivhWH4OoSvzId5Gv9uNBrN7XYLSqmo
|
||||||
gWEY/h3CU+VgFEWHIAgWWZaBfv8GmjqiN5vN0Gw2/wzhyXLlPM/BzezHFrl9g/zLa9DUFr3pdIp6vQ7L
|
c2AQBC9DeKoc9H1/5/v+LEkS0D+/QWNb9CaTCarV6vMQnixXTtMU3Mzu1kitC6Q/P4LGluiNx2NUKhWY
|
||||||
sk4hcRyT3W4nho7Ho0jOrRKY9w6s8RHMvikgk8kEtVrtFBJF0aswDMl2u32AfO2KZHZ/B9b8fAYZj8dw
|
pnkICcOQbDYbMbTf70VyaubB3C9gv76DWRcZZDQaoVwuH0Lq9fqHIAjIer1+hNy2RTK7uQKrXh9BhsMh
|
||||||
XfcUEgSB0mg0yGazeYCsElCzBHb/4RFilUATQ/SHwyFM04Sqqo8Q3/cVz/PIer0+h/ifwNxb/DRvwXIq
|
HMc5hNRqtZzrumS1Wj1CFhGokQe7+fYEMfOgkS76/X4fhmFAUZQniOd5uVKpRJbL5THE+wHmXOLeuARL
|
||||||
trBtG5VK5UUB4PI8T6lWq2S1WhWQo/EGzHmLrHaHA9kXZlVVX56YpVzXVRzHIfwSBWTZB/Xegx2yy2Yp
|
qdjCsiwUi8V3GYDLdd2cbdtksVhkkL3+Ccz+jKR8hR3ZZmZFUd4fmKUcxxEQfokCMu+Cul/Bdslps5Rh
|
||||||
0zQVwzAIv0T5i7nZsqzLZild1691XSf8wEaj0b+ZpTRNu9Y0jfyXWYpDyuXy89/rUr8A4v0dufOO1X4A
|
GDld1wm/RPmLudk0zdNmKU3TzjVNI/zABoPBv5mlVFU9V1WV/JdZikMKhcLbv+tSD5T6HZWMaVplAAAA
|
||||||
AAAASUVORK5CYII=
|
AElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHeSURBVDhPnZLda9NgFMb3T3ir+F8Mwv6e3cgcorfToYgK
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Min/PbmQO0VvdUEQF
|
||||||
Or/unF94ldJsZm3SJq4xTSFJDSlt09oPSilpa1sKilbfsn7wyPtCInG6og+EkHPO83sO5KxVq1VUKhWU
|
nV93bk7xKqXZzNqkzbrGmJKmXUhpm9Z+UEpJW9tSULT6lvWDR94X3kicrugDIeSc8/yeAzkrlUoF5XIZ
|
||||||
y2WUSiUUi0UUCgW4rkvf59ZWyfM8LJfLU0+v12MQwzDOhtBkavB9nyV3Op0QQmuO45wNoSvTYZpGv/P5
|
pVIJxWIRhUIB+XwejuPQ94WVZXJdF4vF4szT7XYZxDCM8yE0mRo8z2PJ7Xbbh9CabdvnQ+jKdJim0e9c
|
||||||
vDuZTDCfz1mdAi3L+juEpgaDtm2fmKbpT6dTjL50kfUOWa/dbiOXy/0ZQpODlReLBah5/PUTHia2cfdg
|
LueMx2PMZjNWp0DLsv4Ooal80DTNU9M0vclkguGXDj64B6zXarWQTqf/DKHJfOX5fA5qHn39hCfRDTzY
|
||||||
E7p3wHqtVgu6rkOW5SjEcRwyHo/Z0Gw2w/BzF3uJy3j2bgcvtZt4lNgOIc1mE5lMJgqxbXvDsiwyGo3Y
|
X4Pu7rNes9mErutQFCUIsW2bjEYjNjSdTjH43MF29BpeJm9hT7uDp9ENH9JoNJBKpYKQbDZ7xbIsMhwO
|
||||||
0Ef/A+4dbuL58Q280e/glXYrAmk0GlAUJQoxTZPLZrNkOByyoXq3gAfiFl4c74aQvaMtZEox1q/VapAk
|
2dBH7wQPD9awe7yJt/p9vNbuBiD1eh2qqgYhmUwmpGkaGQwGbKjWyeOxtI5Xx1s+ZPtwHalimPWr1Spk
|
||||||
CTzP/4IYhsFpmkYGg8EpyOv3t/FUvoonR9cwX8zYFqlUCvF4/EIIoNI0jVNVlfT7/RBy/+0lPE5ewb66
|
WYYgCL8ghmGEkskk6ff7ZyBv3t/DC+UGnh/exGw+ZVvE43FEIpFLPoBK07RQIpEgvV7Phzx6dxXPYtex
|
||||||
i+8/voVmnucvRsyBFEXh0uk0oZfI1vVd7KvXMT0hq82BJEnikskkoZcY/GJqlmV5tTmQKIrroigSemD1
|
c7SF7z+++WZBEC4HzFyqqjIIvUS2rudg5+g2JqdkuZlLluVQLBYj9BL5L6ZmRVGWm7kkSVqVJInQA6vV
|
||||||
ev3fzIEEQVgXBIH8lzkQhcRisfO/1wP9BF1iD3Q2XAPzAAAAAElFTkSuQmCC
|
av9m5hJFcVUURfJfZi4KCYfDF3+vc/0ED18PUDextaQAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGpSURBVDhP7Y87TyJRGIZR9AdYiYWFMdb+AYvNopXRWJho
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y89T1phGIZpgR/QqTiQ6NDZP+BgCiZd2jA0
|
||||||
oY1aWGyzW8DuFmplobGxMxkPrVrY4f0SHJhhmBmGAYMxiIjcxkGh8ZZoeM05UWPG6w/wTZ7m5Hve8302
|
sUNNBx0cjIMDtB3EycGGxY2Evmym7eBGhfgVeuB8cM7hcIDQGKSU7+NBYWlrEw23ed+oMUdr/QHeybW8
|
||||||
23dYPB6PkxASJITgiyQ5jut+LiCExFVVRDabYORylCNGPp9kFArHMIwUDOMEsZhKSzIvC8xUKg5B8MPn
|
ea77fR6b7T4s0WjUQwgRCSG4I5VIJPLisoAQUlJVAc1mmdFqUfYZ7XaF0en8gGFUYRg/USiotKRxtcCs
|
||||||
8zF4noff70cgEIAgCAgGg5AkCaqqshKO424sBQeQJJGJVikUCkFRFCbregSmmXldkE4fQlFkiKLIRCrJ
|
Vkvg+RSSySSD4zikUimk02nwPA9RFCFJElRVZSXhcPjYUvAdkiQw0SplMhkoisJkXc/BNBvXC2q1PSiK
|
||||||
ssykcDiMSCQCXdexvx9DsZh7XXB6moCmqUyiv1FJ0zQmRqNRKHNj2BtswZazFrv9jVj83Xv3JGNnZxPn
|
DEEQmEglWZaZlM1mkcvloOs6isUCut3W9YJ6vQxNU5lEf6OSpmlMzOfzUMKL+Db1BFseJ3ZfubE2//Lk
|
||||||
53lcXFAKKJUMlMtnj5hILE4j6mrDjXcGlfgarhb+QP7ViiX3AFjBZ3h7HLj2zgCzXYC7DphsQnHqB1Z6
|
QsbOziYOD9s4OqJ00OsZ6PcPzjFR/vwBef8YjmMhDEpx/P60AHluFF8Cr8EK/kfM58KfWAhYfQ4EHgHL
|
||||||
6vF8xkfZ+FlTqajLeJnyuAP03Tr7Ztbb7ZnL+WFg3IFblw0llw3pUTvWOuxZ6+yb4fsaJqSh5ru8uwnm
|
I+iujOOr7zEuz7gtG08dg4G6jqvpB12g79bZG5Pw2hu/Pk4DQRf++m3o+W2ozdoRn7A3rbM3hpscWpLe
|
||||||
v1okR6qw3Vlzv9pe/d86+274vsa/6057iq5NN3qSHwBTm6pq5+CDSAAAAABJRU5ErkJggg==
|
DJ+0AyMw3zlRmXmA7WeO0w3vw/fW2X+Gm3S/TXjsVbo23ehCPgMKrqo38mZYEwAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLfT9NXGMdx/oPdL17twmQxi97sajfTzV+b
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLxU9N1HMdx/oN+7/qp7vzF6+qX7vqts9K0
|
||||||
2Q91CxuxZGt0mWMxy3TFMhRx+hUqBVoI0FoqvxxrVSZFwHWtWuy0gy8UykAdiggMEGq/7bftkTB8L222
|
vDKzjvIcepx1GZ3XpQ1HJGL6FRbjKwOBDfg2CKMNJRkChps6JB34hcEWWmGIgIODue/23faRI3x229US
|
||||||
TojbJ3muznNeJznPk2V39ZfZ2+RZs1NWjC3PqeaVVemQFetFuTzrn9ja5bm5R4+eqvE48YT431pQBXOR
|
znrdvX/6vD+Pz93n/c5R3MMVSrs6J7tUzdL8mHKsLtmpavazamXOP2noHJy/Fw4/1BMJEknxv7WoC+aj
|
||||||
GDXOvkgGSL0cU+NUd0xS1hGk6PIPNPSZcQx+x7lgMVU+A5/XW/ja1oPONsJ8TGByDigZoLxFVlJ6zeVJ
|
cWrb/NEskH45rieo6ZqmoitAyfkfcNyw4hz9mtOBUqp9Zj5usvF5Yz/GxnEW4gLZNaJlgcpmVUvrteen
|
||||||
pK5uGnrNtA4dxNr7GTW3tNQG9mNwH2d/XQt6+yhzynOAWEJQ1zVFUWcT3weLqQ3spfKXPRh8HyFdz8bo
|
kXp6cQxZaRs7gH3oI2qv51Pn34e57yj76lsxKTeZ1x4DxJOC+p4ZSrpb+D5QSp1/Lyd/3oXZ9z7SlVws
|
||||||
+4pc02kKG+8wqyQxOftXAkpCYO2e5qirgbNyIRV+DdK1Dznm3cmRn3ciefLQVJRgb23idks2v186QdC2
|
vs/Iq/qG4uZfmdNSyK7h1YCWFNh7ZznsdvCtWow8YEC6/B5HvNv56uJ2JE8BBrkMpa2FW625/H7uGIHG
|
||||||
hWD1Ok0aiMQFtit/ILk6MftOUe7XUuTdhf6ndznqyebbH/OpbjzEjFeHMtQB6jSRofP4S7fNpoHHqsDu
|
TQRq1hsyQDQhaLxwD8ndjdV3gsqBfEq872D66S0Oe3L58sdCapoPEvYa0ca6QJ8lOtaOr3zzXAa4rwuU
|
||||||
nsHcOUi+w47UfoBSz14k9ycUOL/AWJ/H/HARi5PtzFwzkAxd5M8HNxi07RNZqVmHY4IGz2waMbmCfFOs
|
vjDW7lEKnQpS537KPXuR+vZQ5PoES1MBC6ESlqY7CV82kwqe5c87VxmxfShy0rOOxAUOz1wGqXIH+KLU
|
||||||
4aTxTSTDRs6YthEeLmQ5eYPkvTzCv37JaJOWoCVnIWB+b20aWIgK9I530LXu4OC5t7AUvErIvpvh5s08
|
wHHLq0jmDTRUbSYSKmYldZXU7QIig59ysyWfgC130W/dti4DLMYEJuebGNu2cuD069iKXiCo7CD03Ubu
|
||||||
Dh1mWdwkMZaLeJhDbETPaN2OpwPlmzekP7GsuU8JRwW+sauZ8ofa6Ll0gJle3d+XNYiJj4kOF3Cv7m3O
|
Bw+xIq6RnMhD3N1JfNxE6NTWhyOVG5/PfGKF44YWiQl8E5eyNRDsoP/cfsJDxr8vGxBTHxALFXG7/g0U
|
||||||
Nl5QM1MwpoHkCsB7tYSRK4dYTvoQ41qePNyDEjrM3dqtTE2MU+l4ZoxVTnkiHIkSVQXR+BMGvE381qln
|
5YyenYIlA6RWAd5LZYxfOMhKyoeYzOfB3V1owUP8VvcaM1OTyM5HxljtUqci0RgxXRBLPGDE28Iv3SZG
|
||||||
sKuCqcAHJMZzCAfzGTvzPgvT95lPr7L87ypb2+RSy/n+ntRypGRHyRsQe8Bty268+hfx127Hc/L15fpG
|
e2Rm/O+SnNxJJFDIRMPbLM7+wUJmlQf/XWV7h1puax/uTy9HWnaWvQLxO9yy7cBrepKBui14jr+00qS4
|
||||||
l5o6r77Qr1jb5IoMsDrHtS8vLd6sYjFQSeDERlo/XaN269atX933n9n12gtCyl275D61fcldtul6R/4r
|
9fR51Zlhzd6hyllgbY7uXre8dK2aJf9J/Mc20Jr3lN5rXP/c2r7/zLYXnxBS3jPLvSe2LPdVvHylq/DZ
|
||||||
L63ueTZ/AZ9Hi93BxLexAAAAAElFTkSuQmCC
|
p9f2PJq/AD40i0VffXQ/AAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
@@ -245,16 +263,16 @@
|
|||||||
<data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHwSURBVDhP7Y7Ni1JhGMVvXadgWrWzRcTQKgXJVbQQx0/w
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRdQsU5BcRW4cR72E
|
||||||
K7iL1FVBKdYrLW4fM9pdGCPMQpnAC+YqoY240VaTWhnouInIVubqziBFSJEGzaRN5olnIDFx/oMOvPBw
|
H8FtoS6iFinWKwn2MaPdheJikIEJvGCuElpcaKOtTKUMdNxEZBut1Z1BipAiDZrSJvPEM5CYOP9BB154
|
||||||
zu8cXo77r4lsNtsJxti9eDzey2azkCSpwxi7wBhTS5KkkEcZMcT+U9ZoNMdcLlcilUqh2+1CURTUajXE
|
OOd3Di/H/ddEdrv9GGPsbiKR6OVyOUiS1GGMnWOMaSVJUsmjjBhi/ynrdLojTqdzI51Oo9vtQlVV1Go1
|
||||||
YrFtSZLq7XZ7SF6j0QAxxFJnMqDX6y+Jovit1Wr1/H7/M4fDMQoGg5BlGdFoFHSTR1kul+sQS53pgcTa
|
xOPxbUmS6q1Wa0heo9EAMcRSZzJgNBovhsPhr+12u+fz+Z4KgjAKBAKQZRmxWAx0k0eZoigdYqkzPbCR
|
||||||
2tpAluWQ2+1eNBgM36vVKprNJur1OgqFAsijjBhiqTMZ0Ol0N0OhUD+dTt8VBKFiMpn27XY7BEE4eHST
|
TCYHsiwHXS7Xoslk+latVtFsNlGv15HP50EeZcQQS53JgMFguBEMBvuZTOaOKIoVi8WyJwgCRFHcf3ST
|
||||||
RxkxxFJnMqDVas95PJ52Mpns5/P5r8ViEZlMBqIoIhKJHNzldYZXl8/8fmlewFP74v6T5eMPJwMcx/FG
|
RxkxxFJnMqDX68+43e53qVSqryjKl0KhgGw2i0gkgmg0un+X1xleXDr1+/nKAp4Ii3uPlo/enwxwHMeb
|
||||||
o/G80+ncCQQCu+FweMAY++n1eq/4fD7Lo6vW4bs7FzHY3MD4fRl7+dt4E9aOXlhUt6ZHDlXJyis/NjcA
|
zeazDodjx+/374ZCoQFj7KfH47ni9XqtDy5bhm9un8eguInx2zK+P76FVyHd6JlVc3N65ECVbLz6o7gJ
|
||||||
2QOsngTWl/AlsYySmd+eZefquVk1Hr8tYlr9mBrkz7JzVbHyH3YfXwdiagxXOPRWOHRu8Cjb+I+z7Fxt
|
yG5g7TiwvoTPG8soWvjtWXauSiua8fh1AdPqx7Ugf5adq4qNf7/78BoQ12K4yqG3yqFznUfZzn+YZedq
|
||||||
+U49eH3t7K9Pq0v4HF2AEjiCqks1KlmP3p9lD9WW73SkYuF36Nv0o7/lP1AsGrGjxCTVAAAAAElFTkSu
|
y3si8fLq6V8f15bwKbYA1X8I1QuaUcl2+N4se6C2vCejFSu/Q9+mH/0t/wFGlxos/Pd5kgAAAABJRU5E
|
||||||
QmCC
|
rkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnToggleGrid.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnToggleGrid.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
@@ -269,39 +287,18 @@
|
|||||||
<data name="btnFitMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnFitMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKbSURBVDhPhZBdSFNhHMbPZURXEXQTFF2trgKpdWFZWH6c
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tniKh9WBZmM5W
|
||||||
0vmxqUu3+bUskyyMUDqm4pxk5YwsmV1MsM3M6FA0NN1mzo+Dbec9y0ihwvmeQStGpO+xguDwxjvdzXHR
|
es02del1ii7LJAsjtG6pOCdZuiJLZg8TbFMzuiSKlm7m/LPU3XOWkUKF89xBK0ak51pBcDlx1L1cF33h
|
||||||
A+/Ny/N7/s//T1GbCrjK1PyQkQVDphj/pFIOusrltw5DjOsvZrneQnXCl1SCy2AWXBVR/rERBAbOMUGH
|
vBy+n+/v+/sxzIZm3flasatAAF3m6Ey3WZl2c4rfyUX97TmCv/WsNuaLK+jmLPBpUUTsLACzHef4gNOk
|
||||||
XjVrL1Bx/VqGs+vAbF9x1G/LMyu5uMjkODxYauXD6ABYQVoApXpBlOpCcC1nAa7u9/dorNO2/KjvNr21
|
mXRkafztBt7vMIKJtpyIz55pUXNropPX4TybGML7wBI2ACRXQEkuD6KV9Dm0vNd3n7WN29mI965+cxPR
|
||||||
Ce8ysvygEQTDSAUgugggahJEdB2I69cEKF0VRKlUCP/YN9WtAd5OmlXyFHCZYoGBEiYUQQUAosYNWGrY
|
XSDQyYEQ1gCELwKEq6GErwNp9RpE8lUoyXkw9GPPWAsLPI16Qc0zwG2Oznbk8sEwzgIIV63DcuUGXA4k
|
||||||
hOuAiGoBlE55O2jG05kTU/IUP1QuB+16lSBKlxJTEyuQRgJENaTFWEuGyteVIyt5KuA0yeRgxEhAsCJd
|
XAaQfMLToOdfNaRH1Twz08UpAYdJAyX5UmxqbAXaCCJcSlsM1aRqvE16Rc0zb135Cj0YNVIQLMmXaZiI
|
||||||
JmE8RBfifyKqDkGpmAR4rfTWAM5RGuMeahleXM/aaIFq43AEmYGIqgS4Vs6L6PjojdPMWHPG1hXm+rTs
|
8IW1PwmXBJGcQwM8Nt3mgElnXtT/2MCL0mraegtctgaHsQVIuBiilUJRwkcHb6bwQzdSN68w1WYQplqN
|
||||||
XK8OkEMJUCraBKsFca2CwAAizbvIzz2jbdm/3Y3p80qe8tty1TMPCqNvujVWfvnXXiEinSSVBSjpQhCl
|
gB4KIjl7AyyB0koRhQHC7Lvwz139dSd/D1QlT6t5xmfP0E48yoy8aWFt4uKv3TAsH6eVIZKNQYQTKext
|
||||||
Eniqp/DPwkg99jWcwM/rU/XKDGqyK9fst2mi3ltnwER7JkP2JW+0KZ2ZvJMnL75qwpGgHS/P3MWedg1+
|
zvwz11tBvJXHyIuKRJM6gxltyrD47GzEc+cUGK7X8XRf+gark/nRe+nKfH81CQccZHGimYzUs+RlsXan
|
||||||
UaXercygfFZaPW6h2fEOOkaO5bFky+6bmasTbWfxp8lO/PXDIP625MQfJ1rxSOUx2ZUsJJnYK2mGqe4S
|
OoPx2vTa11a9MNSgi9JjjVhTlb7buuXhutPk02gj+fqhk3xbcJGPw7Wk13xEcccLiSfhShI31pJLQpN2
|
||||||
HJ614ej7AbzC3cfzjyqwNUP1+eX5lO1Kf1I9q001eCz5eMndjBfdLXj6XhEeqTn8/anu4A6l958aNh8x
|
EnnfQZb8D8n0kyJiS9F87jufsFXtj6vnZYnciPUMWRi4ReYHasj4g2zSW3rw+zPj/m1q7z/VYznEufIT
|
||||||
OMtS8OvWLDxcqf7i1KfsUnr+q+Gqozv7Sw5ZHKa0bYm/v22YpTjaxhhxAAAAAElFTkSuQmCC
|
yKvaNNJdqP3iMiXsUHv+q57iw9vbcw9YneakLbG/v5ifpNsR5bepAAAAAElFTkSuQmCC
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
|
||||||
<value>249, 17</value>
|
|
||||||
</metadata>
|
|
||||||
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
|
||||||
<value>249, 17</value>
|
|
||||||
</metadata>
|
|
||||||
<data name="btNodeRemove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
|
||||||
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
|
||||||
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
|
||||||
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
|
||||||
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
|
||||||
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
|
||||||
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
|
||||||
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
|
||||||
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
|
||||||
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
@@ -310,7 +307,7 @@
|
|||||||
<data name="btnNew.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnNew.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp
|
wQAADsEBuJFr7QAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp
|
||||||
p73/Tz3pgSz0/+nbryi4c/eb/+EFE8FycANAtk475fV/0nH3//1HXP53HrCHu+TA/b9w/OnbL7ABKIbA
|
p73/Tz3pgSz0/+nbryi4c/eb/+EFE8FycANAtk475fV/0nH3//1HXP53HrCHu+TA/b9w/OnbL7ABKIbA
|
||||||
DJhw1PV/9wGH/y17rP/XbTNHMWD3HQgGGQDDQYnVCANgoGGHxf+qzcbIQnADtt36A8Ybr//BNAAUYCCA
|
DJhw1PV/9wGH/y17rP/XbTNHMWD3HQgGGQDDQYnVCANgoGGHxf+qzcbIQnADtt36A8Ybr//BNAAUYCCA
|
||||||
HIgwZ6NrXn3lN6YBoJBGBzBnwzTCNMMM8AzNBquDGwASxGcrCC+5CMGg2EAxABS3bz5+wzAAm+Z55yAG
|
HIgwZ6NrXn3lN6YBoJBGBzBnwzTCNMMM8AzNBquDGwASxGcrCC+5CMGg2EAxABS3bz5+wzAAm+Z55yAG
|
||||||
@@ -322,7 +319,7 @@
|
|||||||
<data name="btnOpen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnOpen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN
|
wQAADsEBuJFr7QAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN
|
||||||
iIGSqCDSKpSKtlb8QBCHEge1rW1iE733Jmlsmhh6g839vud+nHseSapJXFwFX3g45wzv7z1vT8//Ufwr
|
iIGSqCDSKpSKtlb8QBCHEge1rW1iE733Jmlsmhh6g839vud+nHseSapJXFwFX3g45wzv7z1vT8//Ufwr
|
||||||
X6acGna23p3tyhmn8Hpoo/h0YO/GE1/vH3nr611cPLynDQgvghZjHgAGsGZ27y6po/o+ZFc+hKzfqa1e
|
X6acGna23p3tyhmn8Hpoo/h0YO/GE1/vH3nr611cPLynDQgvghZjHgAGsGZ27y6po/o+ZFc+hKzfqa1e
|
||||||
JtWlkTL/fPBkB3gWkBklsKpxGKVJGJvTMMvzYK4E5ulgVAOjyu7bbYBR2xYWgrk2kH8cUD1HgVFKQi8m
|
JtWlkTL/fPBkB3gWkBklsKpxGKVJGJvTMMvzYK4E5ulgVAOjyu7bbYBR2xYWgrk2kH8cUD1HgVFKQi8m
|
||||||
@@ -337,7 +334,7 @@
|
|||||||
<data name="btnReopen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnReopen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4
|
wQAADsEBuJFr7QAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4
|
||||||
uXTSIYNiRSgFBxU6qHSoCMHiyfPJm9zFgKAHT0Mu9z53+fK15P4wSv5gojcYuvbNo3EU37tW587YaF8a
|
uXTSIYNiRSgFBxU6qHSoCMHiyfPJm9zFgKAHT0Mu9z53+fK15P4wSv5gojcYuvbNo3EU37tW587YaF8a
|
||||||
q3unAYGEG4JInl7e3HKrFsCc5rlunj+7+spuJpFAiw4HiynN7mwqamxX3OsoMUEg4Ydjs0Ds9+cNBFud
|
q3unAYGEG4JInl7e3HKrFsCc5rlunj+7+spuJpFAiw4HiynN7mwqamxX3OsoMUEg4Ydjs0Ds9+cNBFud
|
||||||
yK4SwOg9cbWFtUzA+7Lg4uHTkGgnrqdwf9YbG4UCJiWQhJ0JcwoJjrsfocA/+m8QlmA6WsoEfuH8owoF
|
yK4SwOg9cbWFtUzA+7Lg4uHTkGgnrqdwf9YbG4UCJiWQhJ0JcwoJjrsfocA/+m8QlmA6WsoEfuH8owoF
|
||||||
@@ -372,7 +369,7 @@
|
|||||||
<data name="btnSaveAs.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnSaveAs.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z
|
wQAADsEBuJFr7QAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z
|
||||||
m7CisBYfthcDXLrBlJktUbIBGQ+hAn2cq3uga3lqBxQuUCil9OHc9t7bj2mjy3B+k29OTnK+n9/35Byp
|
m7CisBYfthcDXLrBlJktUbIBGQ+hAn2cq3uga3lqBxQuUCil9OHc9t7bj2mjy3B+k29OTnK+n9/35Byp
|
||||||
+NTowffN7iGDxb1hsLgxWNzC0OoWBotHGMwusafF4dt5fPzHF8uuPyf9n3Y32cfXtsR6NCG0aELhX69v
|
+NTowffN7iGDxb1hsLgxWNzC0OoWBotHGMwusafF4dt5fPzHF8uuPyf9n3Y32cfXtsR6NCG0aELhX69v
|
||||||
5S0IyFvU31yg/MJ06r2q/uf/m5f2Njsi8VRGc81l8SyouOdUHI8zjM4o3PYnUVTonUxT3zPPp+en089A
|
5S0IyFvU31yg/MJ06r2q/uf/m5f2Njsi8VRGc81l8SyouOdUHI8zjM4o3PYnUVTonUxT3zPPp+en089A
|
||||||
@@ -386,6 +383,21 @@
|
|||||||
8maMpXiUaCpB7aVH7Drag7TDOBxajYut0FpaD0XSzP7jfWfG6eiPsZKIYLLto83zJe2eGky2UlaTi9Rd
|
8maMpXiUaCpB7aVH7Drag7TDOBxajYut0FpaD0XSzP7jfWfG6eiPsZKIYLLto83zJe2eGky2UlaTi9Rd
|
||||||
CvLG4etIrxoHvigqHxh+pXIo+nLlIEWVA6KoYkCUnXUy+Ff+FYK4Nr4lkOwikO7CtWEB5mjqCvLm5zfY
|
CvLG4etIrxoHvigqHxh+pXIo+nLlIEWVA6KoYkCUnXUy+Ff+FYK4Nr4lkOwikO7CtWEB5mjqCvLm5zfY
|
||||||
9qme1o7PBh0fmsYxmic4+v08R1pnqTJPU30uwBGzl11VN3jp427+BmRb26lRD0KdAAAAAElFTkSuQmCC
|
9qme1o7PBh0fmsYxmic4+v08R1pnqTJPU30uwBGzl11VN3jp427+BmRb26lRD0KdAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1,825 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Design;
|
|
||||||
using AGVNavigationCore.Models;
|
|
||||||
using AGVNavigationCore.Controls;
|
|
||||||
|
|
||||||
namespace AGVMapEditor.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 노드 타입에 따른 PropertyWrapper 팩토리
|
|
||||||
/// </summary>
|
|
||||||
public static class NodePropertyWrapperFactory
|
|
||||||
{
|
|
||||||
public static object CreateWrapper(MapNode node, List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
switch (node.Type)
|
|
||||||
{
|
|
||||||
case NodeType.Label:
|
|
||||||
return new LabelNodePropertyWrapper(node, mapNodes);
|
|
||||||
case NodeType.Image:
|
|
||||||
return new ImageNodePropertyWrapper(node, mapNodes);
|
|
||||||
default:
|
|
||||||
return new NodePropertyWrapper(node, mapNodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 노드 전용 PropertyWrapper
|
|
||||||
/// </summary>
|
|
||||||
public class LabelNodePropertyWrapper
|
|
||||||
{
|
|
||||||
private MapNode _node;
|
|
||||||
private List<MapNode> _mapNodes;
|
|
||||||
|
|
||||||
public LabelNodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_mapNodes = mapNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 래핑된 MapNode 인스턴스 접근
|
|
||||||
/// </summary>
|
|
||||||
public MapNode WrappedNode => _node;
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 ID")]
|
|
||||||
[Description("노드의 고유 식별자")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public string NodeId
|
|
||||||
{
|
|
||||||
get => _node.NodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 타입")]
|
|
||||||
[Description("노드의 타입")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public NodeType Type
|
|
||||||
{
|
|
||||||
get => _node.Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("텍스트")]
|
|
||||||
[Description("표시할 텍스트")]
|
|
||||||
public string LabelText
|
|
||||||
{
|
|
||||||
get => _node.LabelText;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.LabelText = value ?? "";
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("폰트 패밀리")]
|
|
||||||
[Description("폰트 패밀리")]
|
|
||||||
public string FontFamily
|
|
||||||
{
|
|
||||||
get => _node.FontFamily;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.FontFamily = value ?? "Arial";
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("폰트 크기")]
|
|
||||||
[Description("폰트 크기")]
|
|
||||||
public float FontSize
|
|
||||||
{
|
|
||||||
get => _node.FontSize;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.FontSize = Math.Max(6, Math.Min(72, value));
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("폰트 스타일")]
|
|
||||||
[Description("폰트 스타일")]
|
|
||||||
public FontStyle FontStyle
|
|
||||||
{
|
|
||||||
get => _node.FontStyle;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.FontStyle = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("전경색")]
|
|
||||||
[Description("텍스트 색상")]
|
|
||||||
public Color ForeColor
|
|
||||||
{
|
|
||||||
get => _node.ForeColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.ForeColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("배경색")]
|
|
||||||
[Description("배경 색상")]
|
|
||||||
public Color BackColor
|
|
||||||
{
|
|
||||||
get => _node.BackColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.BackColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("배경 표시")]
|
|
||||||
[Description("배경을 표시할지 여부")]
|
|
||||||
public bool ShowBackground
|
|
||||||
{
|
|
||||||
get => _node.ShowBackground;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.ShowBackground = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("라벨")]
|
|
||||||
[DisplayName("패딩")]
|
|
||||||
[Description("텍스트 주변 여백 (픽셀 단위)")]
|
|
||||||
public int Padding
|
|
||||||
{
|
|
||||||
get => _node.Padding;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Padding = Math.Max(0, Math.Min(50, value));
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("X 좌표")]
|
|
||||||
[Description("맵에서의 X 좌표")]
|
|
||||||
public int PositionX
|
|
||||||
{
|
|
||||||
get => _node.Position.X;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(value, _node.Position.Y);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("Y 좌표")]
|
|
||||||
[Description("맵에서의 Y 좌표")]
|
|
||||||
public int PositionY
|
|
||||||
{
|
|
||||||
get => _node.Position.Y;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(_node.Position.X, value);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("생성 일시")]
|
|
||||||
[Description("노드가 생성된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime CreatedDate
|
|
||||||
{
|
|
||||||
get => _node.CreatedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("수정 일시")]
|
|
||||||
[Description("노드가 마지막으로 수정된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime ModifiedDate
|
|
||||||
{
|
|
||||||
get => _node.ModifiedDate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 노드 전용 PropertyWrapper
|
|
||||||
/// </summary>
|
|
||||||
public class ImageNodePropertyWrapper
|
|
||||||
{
|
|
||||||
private MapNode _node;
|
|
||||||
private List<MapNode> _mapNodes;
|
|
||||||
|
|
||||||
public ImageNodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_mapNodes = mapNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 래핑된 MapNode 인스턴스 접근
|
|
||||||
/// </summary>
|
|
||||||
public MapNode WrappedNode => _node;
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 ID")]
|
|
||||||
[Description("노드의 고유 식별자")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public string NodeId
|
|
||||||
{
|
|
||||||
get => _node.NodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 타입")]
|
|
||||||
[Description("노드의 타입")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public NodeType Type
|
|
||||||
{
|
|
||||||
get => _node.Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("이미지")]
|
|
||||||
[DisplayName("이미지 경로")]
|
|
||||||
[Description("이미지 파일 경로 (... 버튼으로 파일 선택)")]
|
|
||||||
[Editor(typeof(ImagePathEditor), typeof(UITypeEditor))]
|
|
||||||
public string ImagePath
|
|
||||||
{
|
|
||||||
get => _node.ImagePath;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
_node.ImagePath = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 파일이 존재하면 Base64로 변환하여 저장
|
|
||||||
if (System.IO.File.Exists(value))
|
|
||||||
{
|
|
||||||
_node.ConvertImageToBase64(value);
|
|
||||||
_node.LoadImage(); // 이미지 다시 로드
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_node.ImagePath = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("이미지")]
|
|
||||||
[DisplayName("가로 배율")]
|
|
||||||
[Description("가로 배율 (1.0 = 원본 크기)")]
|
|
||||||
public float ScaleWidth
|
|
||||||
{
|
|
||||||
get => _node.Scale.Width;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Scale = new SizeF(Math.Max(0.1f, Math.Min(5.0f, value)), _node.Scale.Height);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("이미지")]
|
|
||||||
[DisplayName("세로 배율")]
|
|
||||||
[Description("세로 배율 (1.0 = 원본 크기)")]
|
|
||||||
public float ScaleHeight
|
|
||||||
{
|
|
||||||
get => _node.Scale.Height;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Scale = new SizeF(_node.Scale.Width, Math.Max(0.1f, Math.Min(5.0f, value)));
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("이미지")]
|
|
||||||
[DisplayName("투명도")]
|
|
||||||
[Description("투명도 (0.0 = 투명, 1.0 = 불투명)")]
|
|
||||||
public float Opacity
|
|
||||||
{
|
|
||||||
get => _node.Opacity;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Opacity = Math.Max(0.0f, Math.Min(1.0f, value));
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("이미지")]
|
|
||||||
[DisplayName("회전각도")]
|
|
||||||
[Description("회전 각도 (도 단위)")]
|
|
||||||
public float Rotation
|
|
||||||
{
|
|
||||||
get => _node.Rotation;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Rotation = value % 360;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("X 좌표")]
|
|
||||||
[Description("맵에서의 X 좌표")]
|
|
||||||
public int PositionX
|
|
||||||
{
|
|
||||||
get => _node.Position.X;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(value, _node.Position.Y);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("Y 좌표")]
|
|
||||||
[Description("맵에서의 Y 좌표")]
|
|
||||||
public int PositionY
|
|
||||||
{
|
|
||||||
get => _node.Position.Y;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(_node.Position.X, value);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("생성 일시")]
|
|
||||||
[Description("노드가 생성된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime CreatedDate
|
|
||||||
{
|
|
||||||
get => _node.CreatedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("수정 일시")]
|
|
||||||
[Description("노드가 마지막으로 수정된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime ModifiedDate
|
|
||||||
{
|
|
||||||
get => _node.ModifiedDate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// PropertyGrid에서 사용할 노드 속성 래퍼 클래스
|
|
||||||
/// </summary>
|
|
||||||
public class NodePropertyWrapper
|
|
||||||
{
|
|
||||||
private MapNode _node;
|
|
||||||
private List<MapNode> _mapNodes;
|
|
||||||
|
|
||||||
public NodePropertyWrapper(MapNode node, List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
_mapNodes = mapNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 래핑된 MapNode 인스턴스 접근
|
|
||||||
/// </summary>
|
|
||||||
public MapNode WrappedNode => _node;
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 ID")]
|
|
||||||
[Description("노드의 고유 식별자")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public string NodeId
|
|
||||||
{
|
|
||||||
get => _node.NodeId;
|
|
||||||
set => _node.NodeId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 이름")]
|
|
||||||
[Description("노드의 표시 이름")]
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _node.Name;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Name = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("노드 타입")]
|
|
||||||
[Description("노드의 타입 (Normal, Rotation, Docking, Charging)")]
|
|
||||||
public NodeType Type
|
|
||||||
{
|
|
||||||
get => _node.Type;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Type = value;
|
|
||||||
_node.CanRotate = value == NodeType.Rotation;
|
|
||||||
_node.SetDefaultColorByType(value);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("기본 정보")]
|
|
||||||
[DisplayName("도킹 방향")]
|
|
||||||
[Description("도킹이 필요한 노드의 경우 AGV 진입 방향 (DontCare: 방향 무관, Forward: 전진 도킹, Backward: 후진 도킹)")]
|
|
||||||
public DockingDirection DockDirection
|
|
||||||
{
|
|
||||||
get => _node.DockDirection;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.DockDirection = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("X 좌표")]
|
|
||||||
[Description("맵에서의 X 좌표")]
|
|
||||||
public int PositionX
|
|
||||||
{
|
|
||||||
get => _node.Position.X;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(value, _node.Position.Y);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("위치")]
|
|
||||||
[DisplayName("Y 좌표")]
|
|
||||||
[Description("맵에서의 Y 좌표")]
|
|
||||||
public int PositionY
|
|
||||||
{
|
|
||||||
get => _node.Position.Y;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.Position = new Point(_node.Position.X, value);
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Category("고급")]
|
|
||||||
[DisplayName("회전 가능")]
|
|
||||||
[Description("이 노드에서 AGV가 회전할 수 있는지 여부")]
|
|
||||||
public bool CanRotate
|
|
||||||
{
|
|
||||||
get => _node.CanRotate;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.CanRotate = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("고급")]
|
|
||||||
[DisplayName("RFID")]
|
|
||||||
[Description("RFID ID")]
|
|
||||||
public string RFID
|
|
||||||
{
|
|
||||||
get => _node.RfidId;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.RfidId = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Category("고급")]
|
|
||||||
[DisplayName("활성화")]
|
|
||||||
[Description("노드 활성화 여부")]
|
|
||||||
public bool IsActive
|
|
||||||
{
|
|
||||||
get => _node.IsActive;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.IsActive = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("노드 배경색")]
|
|
||||||
[Description("노드 배경 색상")]
|
|
||||||
public Color DisplayColor
|
|
||||||
{
|
|
||||||
get => _node.DisplayColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.DisplayColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 색상")]
|
|
||||||
[Description("노드 텍스트 색상 (NodeId, Name 등)")]
|
|
||||||
public Color ForeColor
|
|
||||||
{
|
|
||||||
get => _node.ForeColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.ForeColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 크기")]
|
|
||||||
[Description("노드 텍스트 크기 (픽셀)")]
|
|
||||||
public float TextFontSize
|
|
||||||
{
|
|
||||||
get => _node.TextFontSize;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.TextFontSize = Math.Max(5.0f, Math.Min(20.0f, value));
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 굵게")]
|
|
||||||
[Description("노드 텍스트 볼드 표시")]
|
|
||||||
public bool TextFontBold
|
|
||||||
{
|
|
||||||
get => _node.TextFontBold;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.TextFontBold = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("이름 말풍선 배경색")]
|
|
||||||
[Description("노드 이름 말풍선(하단 표시) 배경색")]
|
|
||||||
public Color NameBubbleBackColor
|
|
||||||
{
|
|
||||||
get => _node.NameBubbleBackColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.NameBubbleBackColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("이름 말풍선 글자색")]
|
|
||||||
[Description("노드 이름 말풍선(하단 표시) 글자색")]
|
|
||||||
public Color NameBubbleForeColor
|
|
||||||
{
|
|
||||||
get => _node.NameBubbleForeColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_node.NameBubbleForeColor = value;
|
|
||||||
_node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("생성 일시")]
|
|
||||||
[Description("노드가 생성된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime CreatedDate
|
|
||||||
{
|
|
||||||
get => _node.CreatedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("수정 일시")]
|
|
||||||
[Description("노드가 마지막으로 수정된 일시")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public DateTime ModifiedDate
|
|
||||||
{
|
|
||||||
get => _node.ModifiedDate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 다중 노드 선택 시 공통 속성 편집용 래퍼
|
|
||||||
/// </summary>
|
|
||||||
public class MultiNodePropertyWrapper
|
|
||||||
{
|
|
||||||
private List<MapNode> _nodes;
|
|
||||||
|
|
||||||
public MultiNodePropertyWrapper(List<MapNode> nodes)
|
|
||||||
{
|
|
||||||
_nodes = nodes ?? new List<MapNode>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("다중 선택")]
|
|
||||||
[DisplayName("선택된 노드 수")]
|
|
||||||
[Description("현재 선택된 노드의 개수")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public int SelectedCount => _nodes.Count;
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("노드 배경색")]
|
|
||||||
[Description("선택된 모든 노드의 배경색을 일괄 변경")]
|
|
||||||
public Color DisplayColor
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].DisplayColor : Color.Blue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
node.DisplayColor = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 색상")]
|
|
||||||
[Description("선택된 모든 노드의 글자 색상을 일괄 변경")]
|
|
||||||
public Color ForeColor
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].ForeColor : Color.Black;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
node.ForeColor = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 크기")]
|
|
||||||
[Description("선택된 모든 노드의 글자 크기를 일괄 변경 (5~20 픽셀)")]
|
|
||||||
public float TextFontSize
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].TextFontSize : 7.0f;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
var validValue = Math.Max(5.0f, Math.Min(20.0f, value));
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 시작: {validValue}, 노드 수: {_nodes.Count}");
|
|
||||||
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontSize} → {validValue}");
|
|
||||||
node.TextFontSize = validValue;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontSize}");
|
|
||||||
}
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 완료");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("글자 굵게")]
|
|
||||||
[Description("선택된 모든 노드의 글자 굵기를 일괄 변경")]
|
|
||||||
public bool TextFontBold
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var result = _nodes.Count > 0 ? _nodes[0].TextFontBold : true;
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold GET: {result}");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 시작: {value}, 노드 수: {_nodes.Count}");
|
|
||||||
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontBold} → {value}");
|
|
||||||
node.TextFontBold = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontBold}");
|
|
||||||
}
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 완료");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("이름 말풍선 배경색")]
|
|
||||||
[Description("선택된 모든 노드의 이름 말풍선(하단 표시) 배경색을 일괄 변경")]
|
|
||||||
public Color NameBubbleBackColor
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].NameBubbleBackColor : Color.Gold;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
node.NameBubbleBackColor = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("표시")]
|
|
||||||
[DisplayName("이름 말풍선 글자색")]
|
|
||||||
[Description("선택된 모든 노드의 이름 말풍선(하단 표시) 글자색을 일괄 변경")]
|
|
||||||
public Color NameBubbleForeColor
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].NameBubbleForeColor : Color.Black;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
node.NameBubbleForeColor = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("고급")]
|
|
||||||
[DisplayName("활성화")]
|
|
||||||
[Description("선택된 모든 노드의 활성화 상태를 일괄 변경")]
|
|
||||||
public bool IsActive
|
|
||||||
{
|
|
||||||
get => _nodes.Count > 0 ? _nodes[0].IsActive : true;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var node in _nodes)
|
|
||||||
{
|
|
||||||
node.IsActive = value;
|
|
||||||
node.ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("안내")]
|
|
||||||
[Description("다중 선택 시 공통 속성만 변경할 수 있습니다")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public string HelpText => "위 속성을 변경하면 선택된 모든 노드에 일괄 적용됩니다.";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 캔버스 속성 래퍼 (배경색 등 캔버스 전체 속성 편집)
|
|
||||||
/// </summary>
|
|
||||||
public class CanvasPropertyWrapper
|
|
||||||
{
|
|
||||||
private UnifiedAGVCanvas _canvas;
|
|
||||||
|
|
||||||
public CanvasPropertyWrapper(UnifiedAGVCanvas canvas)
|
|
||||||
{
|
|
||||||
_canvas = canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("배경")]
|
|
||||||
[DisplayName("배경색")]
|
|
||||||
[Description("맵 캔버스 배경색")]
|
|
||||||
public Color BackgroundColor
|
|
||||||
{
|
|
||||||
get => _canvas.BackColor;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_canvas.BackColor = value;
|
|
||||||
_canvas.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("그리드")]
|
|
||||||
[DisplayName("그리드 표시")]
|
|
||||||
[Description("그리드 표시 여부")]
|
|
||||||
public bool ShowGrid
|
|
||||||
{
|
|
||||||
get => _canvas.ShowGrid;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_canvas.ShowGrid = value;
|
|
||||||
_canvas.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Category("정보")]
|
|
||||||
[DisplayName("설명")]
|
|
||||||
[Description("맵 캔버스 전체 속성")]
|
|
||||||
[ReadOnly(true)]
|
|
||||||
public string Description
|
|
||||||
{
|
|
||||||
get => "빈 공간을 클릭하면 캔버스 전체 속성을 편집할 수 있습니다.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<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')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -48,6 +48,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.VisualBasic" />
|
<Reference Include="Microsoft.VisualBasic" />
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
@@ -58,9 +61,6 @@
|
|||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Controls\AGVState.cs" />
|
<Compile Include="Controls\AGVState.cs" />
|
||||||
@@ -73,11 +73,17 @@
|
|||||||
<DependentUpon>UnifiedAGVCanvas.cs</DependentUpon>
|
<DependentUpon>UnifiedAGVCanvas.cs</DependentUpon>
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Models\AGVCommand.cs" />
|
||||||
<Compile Include="Models\Enums.cs" />
|
<Compile Include="Models\Enums.cs" />
|
||||||
<Compile Include="Models\IMovableAGV.cs" />
|
<Compile Include="Models\IMovableAGV.cs" />
|
||||||
<Compile Include="Models\VirtualAGV.cs" />
|
<Compile Include="Models\VirtualAGV.cs" />
|
||||||
<Compile Include="Models\MapLoader.cs" />
|
<Compile Include="Models\MapLoader.cs" />
|
||||||
|
<Compile Include="Models\MapMagnet.cs" />
|
||||||
|
<Compile Include="Models\MapMark.cs" />
|
||||||
<Compile Include="Models\MapNode.cs" />
|
<Compile Include="Models\MapNode.cs" />
|
||||||
|
<Compile Include="Models\NodeBase.cs" />
|
||||||
|
<Compile Include="Models\MapLabel.cs" />
|
||||||
|
<Compile Include="Models\MapImage.cs" />
|
||||||
<Compile Include="PathFinding\Planning\AGVPathfinder.cs" />
|
<Compile Include="PathFinding\Planning\AGVPathfinder.cs" />
|
||||||
<Compile Include="PathFinding\Planning\DirectionChangePlanner.cs" />
|
<Compile Include="PathFinding\Planning\DirectionChangePlanner.cs" />
|
||||||
<Compile Include="PathFinding\Planning\DirectionalPathfinder.cs" />
|
<Compile Include="PathFinding\Planning\DirectionalPathfinder.cs" />
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
// 이동 경로 정보 추가
|
// 이동 경로 정보 추가
|
||||||
Point? PrevPosition { get; }
|
Point? PrevPosition { get; }
|
||||||
string CurrentNodeId { get; }
|
MapNode CurrentNode { get; }
|
||||||
string PrevNodeId { get; }
|
MapNode PrevNode { get; }
|
||||||
DockingDirection DockingDirection { get; }
|
DockingDirection DockingDirection { get; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
|||||||
|
using AGVNavigationCore.Models;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using AGVNavigationCore.Models;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace AGVNavigationCore.Controls
|
namespace AGVNavigationCore.Controls
|
||||||
{
|
{
|
||||||
@@ -15,7 +17,17 @@ namespace AGVNavigationCore.Controls
|
|||||||
Focus(); // 포커스 설정
|
Focus(); // 포커스 설정
|
||||||
|
|
||||||
var worldPoint = ScreenToWorld(e.Location);
|
var worldPoint = ScreenToWorld(e.Location);
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetItemAt(worldPoint);
|
||||||
|
|
||||||
|
// 에뮬레이터 모드 처리
|
||||||
|
if (_canvasMode == CanvasMode.Emulator)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButtons.Right && hitNode != null)
|
||||||
|
{
|
||||||
|
NodeRightClicked?.Invoke(this, hitNode);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 🔥 어떤 모드에서든 노드/빈 공간 클릭 시 선택 이벤트 발생 (속성창 업데이트)
|
// 🔥 어떤 모드에서든 노드/빈 공간 클릭 시 선택 이벤트 발생 (속성창 업데이트)
|
||||||
bool ctrlPressed = (ModifierKeys & Keys.Control) == Keys.Control;
|
bool ctrlPressed = (ModifierKeys & Keys.Control) == Keys.Control;
|
||||||
@@ -38,7 +50,11 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 마지막 선택된 노드 업데이트 (단일 참조용)
|
// 마지막 선택된 노드 업데이트 (단일 참조용)
|
||||||
_selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null;
|
_selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null;
|
||||||
|
|
||||||
// 다중 선택 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
|
// 단일/다중 선택 이벤트 발생
|
||||||
|
if (_selectedNodes.Count == 1)
|
||||||
|
{
|
||||||
|
NodeSelect?.Invoke(this, _selectedNodes[0], e);
|
||||||
|
}
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
NodesSelected?.Invoke(this, _selectedNodes);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -49,7 +65,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
_selectedNodes.Clear();
|
_selectedNodes.Clear();
|
||||||
_selectedNodes.Add(hitNode);
|
_selectedNodes.Add(hitNode);
|
||||||
|
|
||||||
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
|
// 단일/다중 선택 이벤트 발생
|
||||||
|
NodeSelect?.Invoke(this, hitNode, e);
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
NodesSelected?.Invoke(this, _selectedNodes);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -84,7 +101,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EditMode.Connect:
|
case EditMode.Connect:
|
||||||
HandleConnectClick(hitNode);
|
HandleConnectClick(hitNode as MapNode);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EditMode.Delete:
|
case EditMode.Delete:
|
||||||
@@ -100,51 +117,51 @@ namespace AGVNavigationCore.Controls
|
|||||||
private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
|
private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
var worldPoint = ScreenToWorld(e.Location);
|
var worldPoint = ScreenToWorld(e.Location);
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetItemAt(worldPoint);
|
||||||
|
|
||||||
if (hitNode != null)
|
// 가동 모드에서는 더블클릭 편집 방지
|
||||||
|
if (_canvasMode == CanvasMode.Run) return;
|
||||||
|
|
||||||
|
if (hitNode == null) return;
|
||||||
|
|
||||||
|
if (hitNode.Type == NodeType.Normal)
|
||||||
{
|
{
|
||||||
// 노드 타입별 더블클릭 액션
|
HandleNormalNodeDoubleClick(hitNode as MapNode);
|
||||||
switch (hitNode.Type)
|
|
||||||
{
|
|
||||||
case NodeType.Normal:
|
|
||||||
case NodeType.Rotation:
|
|
||||||
case NodeType.Docking:
|
|
||||||
case NodeType.Charging:
|
|
||||||
HandleNormalNodeDoubleClick(hitNode);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeType.Label:
|
|
||||||
HandleLabelNodeDoubleClick(hitNode);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeType.Image:
|
|
||||||
HandleImageNodeDoubleClick(hitNode);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// 기본 동작: 노드 선택 이벤트 발생
|
|
||||||
_selectedNode = hitNode;
|
|
||||||
_selectedNodes.Clear();
|
|
||||||
_selectedNodes.Add(hitNode);
|
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (hitNode.Type == NodeType.Label)
|
||||||
|
{
|
||||||
|
HandleLabelDoubleClick(hitNode as MapLabel);
|
||||||
|
}
|
||||||
|
else if (hitNode.Type == NodeType.Image)
|
||||||
|
{
|
||||||
|
HandleImageDoubleClick(hitNode as MapImage);
|
||||||
|
}
|
||||||
|
else if (hitNode.Type == NodeType.Mark)
|
||||||
|
{
|
||||||
|
HandleMarkDoubleClick(hitNode as MapMark);
|
||||||
|
}
|
||||||
|
else if (hitNode.Type == NodeType.Magnet)
|
||||||
|
{
|
||||||
|
HandleMagnetDoubleClick(hitNode as MapMagnet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleNormalNodeDoubleClick(MapNode node)
|
private void HandleNormalNodeDoubleClick(MapNode node)
|
||||||
{
|
{
|
||||||
// RFID 입력창 표시
|
// RFID 입력창 표시
|
||||||
string currentRfid = node.RfidId ?? "";
|
var currentRfid = node.RfidId;
|
||||||
string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
|
string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
|
||||||
$"노드 '{node.Name}'의 RFID를 입력하세요:",
|
$"노드 '{node.RfidId}[{node.Id}]'의 RFID를 입력하세요:",
|
||||||
"RFID 설정",
|
"RFID 설정",
|
||||||
currentRfid);
|
currentRfid.ToString());
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(newRfid) && newRfid != currentRfid)
|
if (ushort.TryParse(newRfid, out ushort newrfidvalue) == false) return;
|
||||||
|
if (newrfidvalue < 1) return;
|
||||||
|
|
||||||
|
|
||||||
|
if (newrfidvalue != currentRfid)
|
||||||
{
|
{
|
||||||
node.RfidId = newRfid.Trim();
|
node.RfidId = newrfidvalue;
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -155,11 +172,19 @@ namespace AGVNavigationCore.Controls
|
|||||||
_selectedNodes.Add(node);
|
_selectedNodes.Add(node);
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
NodesSelected?.Invoke(this, _selectedNodes);
|
||||||
}
|
}
|
||||||
|
private void HandleMarkDoubleClick(MapMark label)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
}
|
||||||
|
private void HandleMagnetDoubleClick(MapMagnet label)
|
||||||
|
{
|
||||||
|
//TODO:
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleLabelNodeDoubleClick(MapNode node)
|
private void HandleLabelDoubleClick(MapLabel label)
|
||||||
{
|
{
|
||||||
// 라벨 텍스트 입력창 표시
|
// 라벨 텍스트 입력창 표시
|
||||||
string currentText = node.LabelText ?? "새 라벨";
|
string currentText = label.Text ?? "새 라벨";
|
||||||
string newText = Microsoft.VisualBasic.Interaction.InputBox(
|
string newText = Microsoft.VisualBasic.Interaction.InputBox(
|
||||||
"라벨 텍스트를 입력하세요:",
|
"라벨 텍스트를 입력하세요:",
|
||||||
"라벨 편집",
|
"라벨 편집",
|
||||||
@@ -167,28 +192,24 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(newText) && newText != currentText)
|
if (!string.IsNullOrWhiteSpace(newText) && newText != currentText)
|
||||||
{
|
{
|
||||||
node.LabelText = newText.Trim();
|
label.Text = newText.Trim();
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제)
|
_selectedNode = label;
|
||||||
_selectedNode = node;
|
LabelDoubleClicked?.Invoke(this, label);
|
||||||
_selectedNodes.Clear();
|
Invalidate();
|
||||||
_selectedNodes.Add(node);
|
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleImageNodeDoubleClick(MapNode node)
|
private void HandleImageDoubleClick(MapImage image)
|
||||||
{
|
{
|
||||||
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제)
|
_selectedNode = image;
|
||||||
_selectedNode = node;
|
|
||||||
_selectedNodes.Clear();
|
|
||||||
_selectedNodes.Add(node);
|
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
|
||||||
|
|
||||||
// 이미지 편집 이벤트 발생 (MainForm에서 처리)
|
// 이미지 편집 이벤트 발생 (MainForm에서 처리)
|
||||||
ImageNodeDoubleClicked?.Invoke(this, node);
|
ImageDoubleClicked?.Invoke(this, image);
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnifiedAGVCanvas_MouseDown(object sender, MouseEventArgs e)
|
private void UnifiedAGVCanvas_MouseDown(object sender, MouseEventArgs e)
|
||||||
@@ -199,24 +220,23 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
if (_editMode == EditMode.Move)
|
if (_editMode == EditMode.Move)
|
||||||
{
|
{
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
// 1. 노드 선택 확인
|
||||||
|
var hitNode = GetItemAt(worldPoint);
|
||||||
if (hitNode != null)
|
if (hitNode != null)
|
||||||
{
|
{
|
||||||
_isDragging = true;
|
_isDragging = true;
|
||||||
_isPanning = false; // 🔥 팬 모드 비활성화 - 중요!
|
_isPanning = false;
|
||||||
_selectedNode = hitNode;
|
_selectedNode = hitNode;
|
||||||
_dragStartPosition = hitNode.Position; // 원래 위치 저장 (고스트용)
|
_dragStartPosition = hitNode.Position;
|
||||||
_dragOffset = new Point(
|
_dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y);
|
||||||
worldPoint.X - hitNode.Position.X,
|
_mouseMoveCounter = 0;
|
||||||
worldPoint.Y - hitNode.Position.Y
|
|
||||||
);
|
|
||||||
_mouseMoveCounter = 0; // 디버그: 카운터 리셋
|
|
||||||
Cursor = Cursors.SizeAll;
|
Cursor = Cursors.SizeAll;
|
||||||
Capture = true; // 🔥 마우스 캡처 활성화 - 이게 핵심!
|
Capture = true;
|
||||||
//System.Diagnostics.Debug.WriteLine($"MouseDown: 드래그 시작! Capture={Capture}, isDragging={_isDragging}, isPanning={_isPanning}, Node={hitNode.NodeId}");
|
|
||||||
Invalidate();
|
Invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 팬 시작 (좌클릭 - 모드에 따라)
|
// 팬 시작 (좌클릭 - 모드에 따라)
|
||||||
@@ -238,7 +258,9 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 컨텍스트 메뉴 (편집 모드에서만)
|
// 컨텍스트 메뉴 (편집 모드에서만)
|
||||||
if (_canvasMode == CanvasMode.Edit)
|
if (_canvasMode == CanvasMode.Edit)
|
||||||
{
|
{
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetItemAt(worldPoint);
|
||||||
|
// TODO: 라벨/이미지에 대한 컨텍스트 메뉴도 지원하려면 여기서 hitLabel/hitImage 확인해서 전달
|
||||||
|
// 현재는 ShowContextMenu가 MapNode만 받으므로 노드만 처리
|
||||||
ShowContextMenu(e.Location, hitNode);
|
ShowContextMenu(e.Location, hitNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,9 +276,12 @@ namespace AGVNavigationCore.Controls
|
|||||||
_mouseMoveCounter++;
|
_mouseMoveCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 호버 노드 업데이트
|
// 호버 업데이트
|
||||||
var newHoveredNode = GetNodeAt(worldPoint);
|
var newHoveredNode = GetItemAt(worldPoint);
|
||||||
if (newHoveredNode != _hoveredNode)
|
|
||||||
|
bool hoverChanged = (newHoveredNode != _hoveredNode);
|
||||||
|
|
||||||
|
if (hoverChanged)
|
||||||
{
|
{
|
||||||
_hoveredNode = newHoveredNode;
|
_hoveredNode = newHoveredNode;
|
||||||
Invalidate();
|
Invalidate();
|
||||||
@@ -279,10 +304,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
else if (_isDragging && _canvasMode == CanvasMode.Edit)
|
else if (_isDragging && _canvasMode == CanvasMode.Edit)
|
||||||
{
|
{
|
||||||
// 노드 드래그
|
// 드래그 위치 계산
|
||||||
if (_selectedNode != null)
|
|
||||||
{
|
|
||||||
var oldPosition = _selectedNode.Position;
|
|
||||||
var newPosition = new Point(
|
var newPosition = new Point(
|
||||||
worldPoint.X - _dragOffset.X,
|
worldPoint.X - _dragOffset.X,
|
||||||
worldPoint.Y - _dragOffset.Y
|
worldPoint.Y - _dragOffset.Y
|
||||||
@@ -295,8 +317,18 @@ namespace AGVNavigationCore.Controls
|
|||||||
newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE;
|
newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool moved = false;
|
||||||
|
|
||||||
|
// 노드 드래그
|
||||||
|
if (_selectedNode != null)
|
||||||
|
{
|
||||||
_selectedNode.Position = newPosition;
|
_selectedNode.Position = newPosition;
|
||||||
NodeMoved?.Invoke(this, _selectedNode);
|
NodeMoved?.Invoke(this, _selectedNode);
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved)
|
||||||
|
{
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트
|
Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트
|
||||||
@@ -397,10 +429,21 @@ namespace AGVNavigationCore.Controls
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapNode GetNodeAt(Point worldPoint)
|
private NodeBase GetItemAt(Point worldPoint)
|
||||||
{
|
{
|
||||||
if (_nodes == null) return null;
|
if (_labels != null)
|
||||||
|
{
|
||||||
|
// 역순으로 검사하여 위에 그려진 노드부터 확인
|
||||||
|
for (int i = _labels.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var node = _labels[i];
|
||||||
|
if (IsPointInNode(worldPoint, node))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_nodes != null)
|
||||||
|
{
|
||||||
// 역순으로 검사하여 위에 그려진 노드부터 확인
|
// 역순으로 검사하여 위에 그려진 노드부터 확인
|
||||||
for (int i = _nodes.Count - 1; i >= 0; i--)
|
for (int i = _nodes.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
@@ -408,37 +451,55 @@ namespace AGVNavigationCore.Controls
|
|||||||
if (IsPointInNode(worldPoint, node))
|
if (IsPointInNode(worldPoint, node))
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (_images != null)
|
||||||
|
{
|
||||||
|
// 역순으로 검사하여 위에 그려진 노드부터 확인
|
||||||
|
for (int i = _images.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var node = _images[i];
|
||||||
|
if (IsPointInNode(worldPoint, node))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInNode(Point point, MapNode node)
|
private bool IsPointInNode(Point point, NodeBase node)
|
||||||
{
|
{
|
||||||
switch (node.Type)
|
if (node is MapLabel label)
|
||||||
{
|
{
|
||||||
case NodeType.Label:
|
return IsPointInLabel(point, label);
|
||||||
return IsPointInLabelNode(point, node);
|
|
||||||
case NodeType.Image:
|
|
||||||
return IsPointInImageNode(point, node);
|
|
||||||
default:
|
|
||||||
return IsPointInCircularNode(point, node);
|
|
||||||
}
|
}
|
||||||
|
if (node is MapImage image)
|
||||||
|
{
|
||||||
|
return IsPointInImage(point, image);
|
||||||
|
}
|
||||||
|
// 라벨과 이미지는 별도 리스트로 관리되므로 여기서 처리하지 않음
|
||||||
|
// 하지만 혹시 모를 하위 호환성을 위해 타입 체크는 유지하되,
|
||||||
|
// 실제 로직은 CircularNode 등으로 분기
|
||||||
|
return IsPointInCircularNode(point, node as MapNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInCircularNode(Point point, MapNode node)
|
private bool IsPointInCircularNode(Point point, MapNode node)
|
||||||
{
|
{
|
||||||
switch (node.Type)
|
switch (node.StationType)
|
||||||
{
|
{
|
||||||
case NodeType.Docking:
|
case StationType.Loader:
|
||||||
|
case StationType.UnLoader:
|
||||||
|
case StationType.Clearner:
|
||||||
|
case StationType.Buffer:
|
||||||
return IsPointInPentagon(point, node);
|
return IsPointInPentagon(point, node);
|
||||||
case NodeType.Charging:
|
case StationType.Charger:
|
||||||
return IsPointInTriangle(point, node);
|
return IsPointInTriangle(point, node);
|
||||||
default:
|
default:
|
||||||
return IsPointInCircle(point, node);
|
return IsPointInCircle(point, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInCircle(Point point, MapNode node)
|
private bool IsPointInCircle(Point point, NodeBase node)
|
||||||
{
|
{
|
||||||
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
@@ -451,7 +512,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
return distance <= hitRadius;
|
return distance <= hitRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInPentagon(Point point, MapNode node)
|
private bool IsPointInPentagon(Point point, NodeBase node)
|
||||||
{
|
{
|
||||||
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보
|
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보
|
||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
@@ -472,7 +533,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
return IsPointInPolygon(point, points);
|
return IsPointInPolygon(point, points);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInTriangle(Point point, MapNode node)
|
private bool IsPointInTriangle(Point point, NodeBase node)
|
||||||
{
|
{
|
||||||
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
@@ -517,38 +578,68 @@ namespace AGVNavigationCore.Controls
|
|||||||
return inside;
|
return inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInLabelNode(Point point, MapNode node)
|
private bool IsPointInLabel(Point point, MapLabel label)
|
||||||
{
|
{
|
||||||
var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText;
|
var text = string.IsNullOrEmpty(label.Text) ? label.Id : label.Text;
|
||||||
|
|
||||||
// 임시 Graphics로 텍스트 크기 측정
|
// Graphics 객체 임시 생성 (Using CreateGraphics is faster than new Bitmap)
|
||||||
using (var tempBitmap = new Bitmap(1, 1))
|
using (var g = this.CreateGraphics())
|
||||||
using (var tempGraphics = Graphics.FromImage(tempBitmap))
|
|
||||||
{
|
{
|
||||||
var font = new Font(node.FontFamily, node.FontSize, node.FontStyle);
|
// Font 생성 로직: 사용자 정의 폰트가 있으면 생성, 없으면 기본 폰트 사용 (Dispose 주의)
|
||||||
var textSize = tempGraphics.MeasureString(text, font);
|
Font fontToUse = null;
|
||||||
|
bool shouldDisposeFont = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(label.FontFamily) || label.FontSize <= 0)
|
||||||
|
{
|
||||||
|
fontToUse = this.Font;
|
||||||
|
shouldDisposeFont = false; // 컨트롤 폰트는 Dispose하면 안됨
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fontToUse = new Font(label.FontFamily, label.FontSize, label.FontStyle);
|
||||||
|
shouldDisposeFont = true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
fontToUse = this.Font;
|
||||||
|
shouldDisposeFont = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var textSize = g.MeasureString(text, fontToUse);
|
||||||
|
|
||||||
var textRect = new Rectangle(
|
var textRect = new Rectangle(
|
||||||
(int)(node.Position.X - textSize.Width / 2),
|
(int)(label.Position.X - textSize.Width / 2),
|
||||||
(int)(node.Position.Y - textSize.Height / 2),
|
(int)(label.Position.Y - textSize.Height / 2),
|
||||||
(int)textSize.Width,
|
(int)textSize.Width,
|
||||||
(int)textSize.Height
|
(int)textSize.Height
|
||||||
);
|
);
|
||||||
|
|
||||||
font.Dispose();
|
|
||||||
return textRect.Contains(point);
|
return textRect.Contains(point);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (shouldDisposeFont && fontToUse != null)
|
||||||
|
{
|
||||||
|
fontToUse.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPointInImageNode(Point point, MapNode node)
|
private bool IsPointInImage(Point point, MapImage image)
|
||||||
{
|
{
|
||||||
var displaySize = node.GetDisplaySize();
|
var displaySize = image.GetDisplaySize();
|
||||||
if (displaySize.IsEmpty)
|
if (displaySize.IsEmpty)
|
||||||
displaySize = new Size(50, 50); // 기본 크기
|
displaySize = new Size(50, 50); // 기본 크기
|
||||||
|
|
||||||
var imageRect = new Rectangle(
|
var imageRect = new Rectangle(
|
||||||
node.Position.X - displaySize.Width / 2,
|
image.Position.X - displaySize.Width / 2,
|
||||||
node.Position.Y - displaySize.Height / 2,
|
image.Position.Y - displaySize.Height / 2,
|
||||||
displaySize.Width,
|
displaySize.Width,
|
||||||
displaySize.Height
|
displaySize.Height
|
||||||
);
|
);
|
||||||
@@ -556,6 +647,32 @@ namespace AGVNavigationCore.Controls
|
|||||||
return imageRect.Contains(point);
|
return imageRect.Contains(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private MapLabel GetLabelAt(Point worldPoint)
|
||||||
|
//{
|
||||||
|
// if (_labels == null) return null;
|
||||||
|
// // 역순으로 검사
|
||||||
|
// for (int i = _labels.Count - 1; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
// var label = _labels[i];
|
||||||
|
// if (IsPointInLabel(worldPoint, label))
|
||||||
|
// return label;
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private MapImage GetImageAt(Point worldPoint)
|
||||||
|
//{
|
||||||
|
// if (_images == null) return null;
|
||||||
|
// // 역순으로 검사
|
||||||
|
// for (int i = _images.Count - 1; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
// var image = _images[i];
|
||||||
|
// if (IsPointInImage(worldPoint, image))
|
||||||
|
// return image;
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
private IAGV GetAGVAt(Point worldPoint)
|
private IAGV GetAGVAt(Point worldPoint)
|
||||||
{
|
{
|
||||||
if (_agvList == null) return null;
|
if (_agvList == null) return null;
|
||||||
@@ -575,7 +692,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSelectClick(MapNode hitNode, Point worldPoint)
|
private void HandleSelectClick(NodeBase hitNode, Point worldPoint)
|
||||||
{
|
{
|
||||||
if (hitNode != null)
|
if (hitNode != null)
|
||||||
{
|
{
|
||||||
@@ -590,8 +707,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
// 연결선을 클릭했을 때 삭제 확인
|
// 연결선을 클릭했을 때 삭제 확인
|
||||||
var (fromNode, toNode) = connection.Value;
|
var (fromNode, toNode) = connection.Value;
|
||||||
string fromDisplay = !string.IsNullOrEmpty(fromNode.RfidId) ? fromNode.RfidId : fromNode.NodeId;
|
string fromDisplay = fromNode.HasRfid() ? fromNode.RfidId.ToString("0000") : fromNode.Id;
|
||||||
string toDisplay = !string.IsNullOrEmpty(toNode.RfidId) ? toNode.RfidId : toNode.NodeId;
|
string toDisplay = toNode.HasRfid() ? toNode.RfidId.ToString("0000") : toNode.Id;
|
||||||
|
|
||||||
var result = MessageBox.Show(
|
var result = MessageBox.Show(
|
||||||
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
|
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
|
||||||
@@ -602,13 +719,13 @@ namespace AGVNavigationCore.Controls
|
|||||||
if (result == DialogResult.Yes)
|
if (result == DialogResult.Yes)
|
||||||
{
|
{
|
||||||
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
|
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
|
||||||
if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
|
if (fromNode.ConnectedNodes.Contains(toNode.Id))
|
||||||
{
|
{
|
||||||
fromNode.RemoveConnection(toNode.NodeId);
|
fromNode.RemoveConnection(toNode.Id);
|
||||||
}
|
}
|
||||||
else if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
|
else if (toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||||
{
|
{
|
||||||
toNode.RemoveConnection(fromNode.NodeId);
|
toNode.RemoveConnection(fromNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이벤트 발생
|
// 이벤트 발생
|
||||||
@@ -645,9 +762,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
var newNode = new MapNode
|
var newNode = new MapNode
|
||||||
{
|
{
|
||||||
NodeId = newNodeId,
|
Id = newNodeId,
|
||||||
Position = worldPoint,
|
Position = worldPoint
|
||||||
Type = NodeType.Normal
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_nodes.Add(newNode);
|
_nodes.Add(newNode);
|
||||||
@@ -666,20 +782,22 @@ namespace AGVNavigationCore.Controls
|
|||||||
worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
|
worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 고유한 NodeId 생성
|
// 고유한 NodeId 생성 (라벨도 ID 공유 권장)
|
||||||
string newNodeId = GenerateUniqueNodeId();
|
string newNodeId = GenerateUniqueNodeId();
|
||||||
|
|
||||||
var newNode = new MapNode
|
var newLabel = new MapLabel
|
||||||
{
|
{
|
||||||
NodeId = newNodeId,
|
Id = newNodeId,
|
||||||
Position = worldPoint,
|
Position = worldPoint,
|
||||||
Type = NodeType.Label,
|
Text = "New Label",
|
||||||
Name = "새 라벨"
|
FontSize = 10,
|
||||||
|
FontFamily = "Arial"
|
||||||
};
|
};
|
||||||
|
|
||||||
_nodes.Add(newNode);
|
if (_labels == null) _labels = new List<MapLabel>();
|
||||||
|
_labels.Add(newLabel);
|
||||||
|
|
||||||
NodeAdded?.Invoke(this, newNode);
|
//NodeAdded?.Invoke(this, newNode); // TODO: 라벨 추가 이벤트 필요?
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -696,17 +814,17 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 고유한 NodeId 생성
|
// 고유한 NodeId 생성
|
||||||
string newNodeId = GenerateUniqueNodeId();
|
string newNodeId = GenerateUniqueNodeId();
|
||||||
|
|
||||||
var newNode = new MapNode
|
var newImage = new MapImage
|
||||||
{
|
{
|
||||||
NodeId = newNodeId,
|
Id = newNodeId,
|
||||||
Position = worldPoint,
|
Position = worldPoint,
|
||||||
Type = NodeType.Image,
|
Name = "New Image"
|
||||||
Name = "새 이미지"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_nodes.Add(newNode);
|
if (_images == null) _images = new List<MapImage>();
|
||||||
|
_images.Add(newImage);
|
||||||
|
|
||||||
NodeAdded?.Invoke(this, newNode);
|
//NodeAdded?.Invoke(this, newNode); // TODO: 이미지 추가 이벤트 필요?
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -724,7 +842,9 @@ namespace AGVNavigationCore.Controls
|
|||||||
nodeId = $"N{counter:D3}";
|
nodeId = $"N{counter:D3}";
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
while (_nodes.Any(n => n.NodeId == nodeId));
|
while (_nodes.Any(n => n.Id == nodeId) ||
|
||||||
|
(_labels != null && _labels.Any(l => l.Id == nodeId)) ||
|
||||||
|
(_images != null && _images.Any(i => i.Id == nodeId)));
|
||||||
|
|
||||||
_nodeCounter = counter;
|
_nodeCounter = counter;
|
||||||
return nodeId;
|
return nodeId;
|
||||||
@@ -761,7 +881,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 연결된 모든 연결선도 제거
|
// 연결된 모든 연결선도 제거
|
||||||
foreach (var node in _nodes)
|
foreach (var node in _nodes)
|
||||||
{
|
{
|
||||||
node.RemoveConnection(hitNode.NodeId);
|
node.RemoveConnection(hitNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodes.Remove(hitNode);
|
_nodes.Remove(hitNode);
|
||||||
@@ -777,13 +897,13 @@ namespace AGVNavigationCore.Controls
|
|||||||
private void CreateConnection(MapNode fromNode, MapNode toNode)
|
private void CreateConnection(MapNode fromNode, MapNode toNode)
|
||||||
{
|
{
|
||||||
// 중복 연결 체크 (양방향)
|
// 중복 연결 체크 (양방향)
|
||||||
if (fromNode.ConnectedNodes.Contains(toNode.NodeId) ||
|
if (fromNode.ConnectedNodes.Contains(toNode.Id) ||
|
||||||
toNode.ConnectedNodes.Contains(fromNode.NodeId))
|
toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
|
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
|
||||||
fromNode.AddConnection(toNode.NodeId);
|
fromNode.AddConnection(toNode.Id);
|
||||||
toNode.AddConnection(fromNode.NodeId);
|
toNode.AddConnection(fromNode.Id);
|
||||||
|
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
@@ -795,20 +915,26 @@ namespace AGVNavigationCore.Controls
|
|||||||
return (float)Math.Sqrt(dx * dx + dy * dy);
|
return (float)Math.Sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowContextMenu(Point location, MapNode hitNode)
|
private void ShowContextMenu(Point location, NodeBase hitItem)
|
||||||
{
|
{
|
||||||
_contextMenu.Items.Clear();
|
_contextMenu.Items.Clear();
|
||||||
|
|
||||||
if (hitNode != null)
|
if (hitItem != null)
|
||||||
{
|
{
|
||||||
_contextMenu.Items.Add("노드 속성...", null, (s, e) =>
|
string typeName = "항목";
|
||||||
|
if (hitItem is MapNode) typeName = "노드";
|
||||||
|
else if (hitItem is MapLabel) typeName = "라벨";
|
||||||
|
else if (hitItem is MapImage) typeName = "이미지";
|
||||||
|
|
||||||
|
_contextMenu.Items.Add($"{typeName} 속성...", null, (s, e) =>
|
||||||
{
|
{
|
||||||
_selectedNode = hitNode;
|
_selectedNode = hitItem;
|
||||||
_selectedNodes.Clear();
|
_selectedNodes.Clear();
|
||||||
_selectedNodes.Add(hitNode);
|
_selectedNodes.Add(hitItem);
|
||||||
NodesSelected?.Invoke(this, _selectedNodes);
|
NodesSelected?.Invoke(this, _selectedNodes);
|
||||||
|
Invalidate();
|
||||||
});
|
});
|
||||||
_contextMenu.Items.Add("노드 삭제", null, (s, e) => HandleDeleteClick(hitNode));
|
_contextMenu.Items.Add($"{typeName} 삭제", null, (s, e) => HandleDeleteClick(hitItem));
|
||||||
_contextMenu.Items.Add("-");
|
_contextMenu.Items.Add("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,13 +959,13 @@ namespace AGVNavigationCore.Controls
|
|||||||
var (fromNode, toNode) = connection.Value;
|
var (fromNode, toNode) = connection.Value;
|
||||||
|
|
||||||
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
|
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
|
||||||
if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
|
if (fromNode.ConnectedNodes.Contains(toNode.Id))
|
||||||
{
|
{
|
||||||
fromNode.RemoveConnection(toNode.NodeId);
|
fromNode.RemoveConnection(toNode.Id);
|
||||||
}
|
}
|
||||||
else if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
|
else if (toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||||
{
|
{
|
||||||
toNode.RemoveConnection(fromNode.NodeId);
|
toNode.RemoveConnection(fromNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이벤트 발생
|
// 이벤트 발생
|
||||||
@@ -852,13 +978,14 @@ namespace AGVNavigationCore.Controls
|
|||||||
private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
|
private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
|
||||||
{
|
{
|
||||||
const int CONNECTION_HIT_TOLERANCE = 10;
|
const int CONNECTION_HIT_TOLERANCE = 10;
|
||||||
|
if (_nodes == null) return null;
|
||||||
|
|
||||||
// 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
|
// 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
|
||||||
foreach (var fromNode in _nodes)
|
foreach (var fromNode in _nodes)
|
||||||
{
|
{
|
||||||
foreach (var toNodeId in fromNode.ConnectedNodes)
|
foreach (var toNodeId in fromNode.ConnectedNodes)
|
||||||
{
|
{
|
||||||
var toNode = _nodes.FirstOrDefault(n => n.NodeId == toNodeId);
|
var toNode = _nodes.FirstOrDefault(n => n.Id == toNodeId);
|
||||||
if (toNode != null)
|
if (toNode != null)
|
||||||
{
|
{
|
||||||
// 연결선과 클릭 위치 간의 거리 계산
|
// 연결선과 클릭 위치 간의 거리 계산
|
||||||
@@ -874,6 +1001,49 @@ namespace AGVNavigationCore.Controls
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleDeleteClick(NodeBase item)
|
||||||
|
{
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
if (item is MapNode hitNode)
|
||||||
|
{
|
||||||
|
// 연결된 모든 연결선도 제거
|
||||||
|
foreach (var node in _nodes)
|
||||||
|
{
|
||||||
|
node.RemoveConnection(hitNode.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodes.Remove(hitNode);
|
||||||
|
|
||||||
|
if (_selectedNode == hitNode)
|
||||||
|
_selectedNode = null;
|
||||||
|
|
||||||
|
NodeDeleted?.Invoke(this, hitNode);
|
||||||
|
}
|
||||||
|
else if (item is MapLabel label)
|
||||||
|
{
|
||||||
|
if (_labels != null) _labels.Remove(label);
|
||||||
|
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
|
||||||
|
}
|
||||||
|
else if (item is MapImage image)
|
||||||
|
{
|
||||||
|
if (_images != null) _images.Remove(image);
|
||||||
|
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
|
||||||
|
}
|
||||||
|
else if (item is MapMark mark)
|
||||||
|
{
|
||||||
|
if (_marks != null) _marks.Remove(mark);
|
||||||
|
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
|
||||||
|
}
|
||||||
|
else if (item is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
if (_magnets != null) _magnets.Remove(magnet);
|
||||||
|
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
|
||||||
|
}
|
||||||
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private float CalculatePointToLineDistance(Point point, Point lineStart, Point lineEnd)
|
private float CalculatePointToLineDistance(Point point, Point lineStart, Point lineEnd)
|
||||||
{
|
{
|
||||||
// 점에서 선분까지의 거리 계산
|
// 점에서 선분까지의 거리 계산
|
||||||
@@ -913,27 +1083,21 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
string tooltipText = "";
|
string tooltipText = "";
|
||||||
|
|
||||||
// 노드 툴팁
|
var hitNode = GetItemAt(worldPoint);
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
|
||||||
if (hitNode != null)
|
|
||||||
{
|
|
||||||
tooltipText = $"노드: {hitNode.NodeId}\n타입: {hitNode.Type}\n위치: ({hitNode.Position.X}, {hitNode.Position.Y})";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// AGV 툴팁
|
|
||||||
var hitAGV = GetAGVAt(worldPoint);
|
var hitAGV = GetAGVAt(worldPoint);
|
||||||
if (hitAGV != null)
|
|
||||||
|
if (hitNode != null)
|
||||||
|
tooltipText = $"노드: {hitNode.Id}\n타입: {hitNode.Type}\n위치: ({hitNode.Position.X}, {hitNode.Position.Y})";
|
||||||
|
else if (hitAGV != null)
|
||||||
{
|
{
|
||||||
var state = _agvStates.ContainsKey(hitAGV.AgvId) ? _agvStates[hitAGV.AgvId] : AGVState.Idle;
|
var state = _agvStates.ContainsKey(hitAGV.AgvId) ? _agvStates[hitAGV.AgvId] : AGVState.Idle;
|
||||||
tooltipText = $"AGV: {hitAGV.AgvId}\n상태: {state}\n배터리: {hitAGV.BatteryLevel:F1}%\n위치: ({hitAGV.CurrentPosition.X}, {hitAGV.CurrentPosition.Y})";
|
tooltipText = $"AGV: {hitAGV.AgvId}\n상태: {state}\n배터리: {hitAGV.BatteryLevel:F1}%\n위치: ({hitAGV.CurrentPosition.X}, {hitAGV.CurrentPosition.Y})";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 툴팁 업데이트 (기존 ToolTip 컨트롤 사용)
|
// 툴팁 텍스트 갱신 (변경되었을 때만)
|
||||||
if (!string.IsNullOrEmpty(tooltipText))
|
if (_tooltip != null && _tooltip.GetToolTip(this) != tooltipText)
|
||||||
{
|
{
|
||||||
// ToolTip 설정 (필요시 추가 구현)
|
_tooltip.SetToolTip(this, tooltipText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,7 +1165,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void PanToNode(string nodeId)
|
public void PanToNode(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _nodes?.FirstOrDefault(n => n.NodeId == nodeId);
|
var node = _nodes?.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
PanTo(node.Position);
|
PanTo(node.Position);
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Drawing2D;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using AGVNavigationCore.Models;
|
using AGVNavigationCore.Models;
|
||||||
using AGVNavigationCore.PathFinding;
|
using AGVNavigationCore.PathFinding;
|
||||||
using AGVNavigationCore.PathFinding.Core;
|
using AGVNavigationCore.PathFinding.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace AGVNavigationCore.Controls
|
namespace AGVNavigationCore.Controls
|
||||||
{
|
{
|
||||||
@@ -21,7 +23,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
private const int NODE_SIZE = 24;
|
private const int NODE_SIZE = 24;
|
||||||
private const int NODE_RADIUS = NODE_SIZE / 2;
|
private const int NODE_RADIUS = NODE_SIZE / 2;
|
||||||
private const int GRID_SIZE = 20;
|
private const int GRID_SIZE = 20;
|
||||||
private const float CONNECTION_WIDTH = 2.0f;
|
private const float CONNECTION_WIDTH = 1.0f;
|
||||||
private const int SNAP_DISTANCE = 10;
|
private const int SNAP_DISTANCE = 10;
|
||||||
private const int AGV_SIZE = 40;
|
private const int AGV_SIZE = 40;
|
||||||
private const int CONNECTION_ARROW_SIZE = 8;
|
private const int CONNECTION_ARROW_SIZE = 8;
|
||||||
@@ -35,7 +37,10 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum CanvasMode
|
public enum CanvasMode
|
||||||
{
|
{
|
||||||
Edit // 편집 가능 (맵 에디터)
|
Edit, // 편집 가능 (맵 에디터)
|
||||||
|
Sync, // 동기화 모드 (장비 설정 동기화)
|
||||||
|
Emulator, // 에뮬레이터 모드
|
||||||
|
Run // 가동 모드 (User Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -63,10 +68,19 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
// 맵 데이터
|
// 맵 데이터
|
||||||
private List<MapNode> _nodes;
|
private List<MapNode> _nodes;
|
||||||
private MapNode _selectedNode;
|
private List<MapLabel> _labels; // 추가
|
||||||
private List<MapNode> _selectedNodes; // 다중 선택
|
private List<MapImage> _images; // 추가
|
||||||
private MapNode _hoveredNode;
|
private List<MapMark> _marks;
|
||||||
private MapNode _destinationNode;
|
private List<MapMagnet> _magnets;
|
||||||
|
|
||||||
|
// 선택된 객체들 (나중에 NodeBase로 통일 필요)
|
||||||
|
private NodeBase _selectedNode;
|
||||||
|
|
||||||
|
private List<NodeBase> _selectedNodes; // 다중 선택 (NodeBase로 변경 고려)
|
||||||
|
|
||||||
|
private NodeBase _hoveredNode;
|
||||||
|
|
||||||
|
private NodeBase _destinationNode;
|
||||||
|
|
||||||
// AGV 관련
|
// AGV 관련
|
||||||
private List<IAGV> _agvList;
|
private List<IAGV> _agvList;
|
||||||
@@ -116,6 +130,11 @@ namespace AGVNavigationCore.Controls
|
|||||||
// RFID 중복 검사
|
// RFID 중복 검사
|
||||||
private HashSet<string> _duplicateRfidNodes = new HashSet<string>();
|
private HashSet<string> _duplicateRfidNodes = new HashSet<string>();
|
||||||
|
|
||||||
|
// 동기화 모드 관련
|
||||||
|
private string _syncMessage = "동기화 중...";
|
||||||
|
private float _syncProgress = 0.0f;
|
||||||
|
private string _syncDetail = "";
|
||||||
|
|
||||||
// 브러쉬 및 펜
|
// 브러쉬 및 펜
|
||||||
private Brush _normalNodeBrush;
|
private Brush _normalNodeBrush;
|
||||||
private Brush _rotationNodeBrush;
|
private Brush _rotationNodeBrush;
|
||||||
@@ -136,27 +155,38 @@ namespace AGVNavigationCore.Controls
|
|||||||
private Pen _pathPen;
|
private Pen _pathPen;
|
||||||
private Pen _agvPen;
|
private Pen _agvPen;
|
||||||
private Pen _highlightedConnectionPen;
|
private Pen _highlightedConnectionPen;
|
||||||
|
private Pen _magnetPen;
|
||||||
|
private Pen _markPen;
|
||||||
|
private ToolTip _tooltip;
|
||||||
|
|
||||||
// 컨텍스트 메뉴
|
// 컨텍스트 메뉴
|
||||||
private ContextMenuStrip _contextMenu;
|
private ContextMenuStrip _contextMenu;
|
||||||
|
|
||||||
|
// 이벤트
|
||||||
|
public event EventHandler<NodeBase> NodeRightClicked;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
// 맵 편집 이벤트
|
// 맵 편집 이벤트
|
||||||
public event EventHandler<MapNode> NodeAdded;
|
public delegate void NodeSelectHandler(object sender, NodeBase node, MouseEventArgs e);
|
||||||
public event EventHandler<List<MapNode>> NodesSelected; // 다중 선택 이벤트
|
public event NodeSelectHandler NodeSelect;
|
||||||
public event EventHandler<MapNode> NodeDeleted;
|
|
||||||
public event EventHandler<MapNode> NodeMoved;
|
public event EventHandler<NodeBase> NodeAdded;
|
||||||
|
public event EventHandler<List<NodeBase>> NodesSelected; // 다중 선택 이벤트
|
||||||
|
public event EventHandler<NodeBase> NodeDeleted;
|
||||||
|
public event EventHandler<NodeBase> NodeMoved;
|
||||||
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
|
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
|
||||||
public event EventHandler<MapNode> ImageNodeDoubleClicked;
|
public event EventHandler<MapImage> ImageDoubleClicked;
|
||||||
|
public event EventHandler<MapLabel> LabelDoubleClicked;
|
||||||
public event EventHandler MapChanged;
|
public event EventHandler MapChanged;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
|
public string PredictMessage { get; set; } = "";
|
||||||
public string MapFileName { get; set; } = "";
|
public string MapFileName { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -173,6 +203,60 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveItem(NodeBase item)
|
||||||
|
{
|
||||||
|
if (item is MapImage img) RemoveImage(img);
|
||||||
|
else if (item is MapLabel lb) RemoveLabel(lb);
|
||||||
|
else if (item is MapNode nd) RemoveNode(nd);
|
||||||
|
else if (item is MapMark mk) RemoveMark(mk);
|
||||||
|
else if (item is MapMagnet mg) RemoveMagnet(mg);
|
||||||
|
else throw new Exception("unknown type");
|
||||||
|
|
||||||
|
}
|
||||||
|
public void RemoveNode(MapNode node)
|
||||||
|
{
|
||||||
|
if (_nodes != null && _nodes.Contains(node))
|
||||||
|
{
|
||||||
|
_nodes.Remove(node);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void RemoveLabel(MapLabel label)
|
||||||
|
{
|
||||||
|
if (_labels != null && _labels.Contains(label))
|
||||||
|
{
|
||||||
|
_labels.Remove(label);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveImage(MapImage image)
|
||||||
|
{
|
||||||
|
if (_images != null && _images.Contains(image))
|
||||||
|
{
|
||||||
|
_images.Remove(image);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveMark(MapMark mark)
|
||||||
|
{
|
||||||
|
if (_marks != null && _marks.Contains(mark))
|
||||||
|
{
|
||||||
|
_marks.Remove(mark);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveMagnet(MapMagnet magnet)
|
||||||
|
{
|
||||||
|
if (_magnets != null && _magnets.Contains(magnet))
|
||||||
|
{
|
||||||
|
_magnets.Remove(magnet);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 편집 모드 (CanvasMode.Edit일 때만 적용)
|
/// 편집 모드 (CanvasMode.Edit일 때만 적용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -219,15 +303,80 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public MapImage SelectedImage
|
||||||
|
{
|
||||||
|
get { return this._selectedNode as MapImage; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public MapLabel SelectedLabel
|
||||||
|
{
|
||||||
|
get { return this._selectedNode as MapLabel; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public MapMark SelectedMark
|
||||||
|
{
|
||||||
|
get { return this._selectedNode as MapMark; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public MapMagnet SelectedMagnet
|
||||||
|
{
|
||||||
|
get { return this._selectedNode as MapMagnet; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 선택된 노드 (단일)
|
/// 선택된 노드 (단일)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MapNode SelectedNode => _selectedNode;
|
public MapNode SelectedNode
|
||||||
|
{
|
||||||
|
get { return this._selectedNode as MapNode; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 선택된 노드들 (다중)
|
/// 선택된 노드들 (다중)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MapNode> SelectedNodes => _selectedNodes ?? new List<MapNode>();
|
public List<NodeBase> SelectedNodes => _selectedNodes ?? new List<NodeBase>();
|
||||||
|
|
||||||
|
|
||||||
|
public List<NodeBase> Items
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<NodeBase> items = new List<NodeBase>();
|
||||||
|
if (Nodes != null && Nodes.Any()) items.AddRange(Nodes);
|
||||||
|
if (Labels != null && Labels.Any()) items.AddRange(Labels);
|
||||||
|
if (Images != null && Images.Any()) items.AddRange(Images);
|
||||||
|
if (Marks != null && Marks.Any()) items.AddRange(Marks);
|
||||||
|
if (Magnets != null && Magnets.Any()) items.AddRange(Magnets);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map file loading 결과를 셋팅합니다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
public void SetMapLoadResult(MapLoader.MapLoadResult result)
|
||||||
|
{
|
||||||
|
this.Nodes = result.Nodes;
|
||||||
|
this.Labels = result.Labels; // 추가
|
||||||
|
this.Images = result.Images; // 추가
|
||||||
|
this.Marks = result.Marks;
|
||||||
|
this.Magnets = result.Magnets;
|
||||||
|
|
||||||
|
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
|
||||||
|
if (result.Settings != null)
|
||||||
|
{
|
||||||
|
this.BackColor = Color.FromArgb(result.Settings.BackgroundColorArgb);
|
||||||
|
this.ShowGrid = result.Settings.ShowGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.FitToNodes();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 노드 목록
|
/// 노드 목록
|
||||||
@@ -249,6 +398,58 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 라벨 목록
|
||||||
|
/// </summary>
|
||||||
|
public List<MapLabel> Labels
|
||||||
|
{
|
||||||
|
get => _labels ?? new List<MapLabel>();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_labels = value ?? new List<MapLabel>();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이미지 목록
|
||||||
|
/// </summary>
|
||||||
|
public List<MapImage> Images
|
||||||
|
{
|
||||||
|
get => _images ?? new List<MapImage>();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_images = value ?? new List<MapImage>();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 마크 목록
|
||||||
|
/// </summary>
|
||||||
|
public List<MapMark> Marks
|
||||||
|
{
|
||||||
|
get => _marks ?? new List<MapMark>();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_marks = value ?? new List<MapMark>();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 마그넷 목록
|
||||||
|
/// </summary>
|
||||||
|
public List<MapMagnet> Magnets
|
||||||
|
{
|
||||||
|
get => _magnets ?? new List<MapMagnet>();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_magnets = value ?? new List<MapMagnet>();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV 목록
|
/// AGV 목록
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -378,7 +579,12 @@ namespace AGVNavigationCore.Controls
|
|||||||
ControlStyles.ResizeRedraw, true);
|
ControlStyles.ResizeRedraw, true);
|
||||||
|
|
||||||
_nodes = new List<MapNode>();
|
_nodes = new List<MapNode>();
|
||||||
_selectedNodes = new List<MapNode>(); // 다중 선택 리스트 초기화
|
_labels = new List<MapLabel>();
|
||||||
|
_images = new List<MapImage>();
|
||||||
|
_marks = new List<MapMark>();
|
||||||
|
_magnets = new List<MapMagnet>();
|
||||||
|
|
||||||
|
_selectedNodes = new List<NodeBase>(); // 다중 선택 리스트 초기화
|
||||||
_agvList = new List<IAGV>();
|
_agvList = new List<IAGV>();
|
||||||
_agvPositions = new Dictionary<string, Point>();
|
_agvPositions = new Dictionary<string, Point>();
|
||||||
_agvDirections = new Dictionary<string, AgvDirection>();
|
_agvDirections = new Dictionary<string, AgvDirection>();
|
||||||
@@ -388,6 +594,12 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
InitializeBrushesAndPens();
|
InitializeBrushesAndPens();
|
||||||
CreateContextMenu();
|
CreateContextMenu();
|
||||||
|
|
||||||
|
_tooltip = new ToolTip();
|
||||||
|
_tooltip.AutoPopDelay = 5000;
|
||||||
|
_tooltip.InitialDelay = 1000;
|
||||||
|
_tooltip.ReshowDelay = 500;
|
||||||
|
_tooltip.ShowAlways = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeBrushesAndPens()
|
private void InitializeBrushesAndPens()
|
||||||
@@ -409,7 +621,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
_gridBrush = new SolidBrush(Color.LightGray);
|
_gridBrush = new SolidBrush(Color.LightGray);
|
||||||
|
|
||||||
// 펜
|
// 펜
|
||||||
_connectionPen = new Pen(Color.DarkBlue, CONNECTION_WIDTH);
|
_connectionPen = new Pen(Color.White, CONNECTION_WIDTH);
|
||||||
|
_connectionPen.DashStyle = DashStyle.Dash;
|
||||||
_connectionPen.EndCap = LineCap.ArrowAnchor;
|
_connectionPen.EndCap = LineCap.ArrowAnchor;
|
||||||
|
|
||||||
_gridPen = new Pen(Color.LightGray, 1);
|
_gridPen = new Pen(Color.LightGray, 1);
|
||||||
@@ -419,6 +632,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
_pathPen = new Pen(Color.Purple, 3);
|
_pathPen = new Pen(Color.Purple, 3);
|
||||||
_agvPen = new Pen(Color.Red, 3);
|
_agvPen = new Pen(Color.Red, 3);
|
||||||
_highlightedConnectionPen = new Pen(Color.Red, 4) { DashStyle = DashStyle.Solid };
|
_highlightedConnectionPen = new Pen(Color.Red, 4) { DashStyle = DashStyle.Solid };
|
||||||
|
_magnetPen = new Pen(Color.FromArgb(100,Color.LightSkyBlue), 15) { DashStyle = DashStyle.Solid };
|
||||||
|
_markPen = new Pen(Color.White, 3); // 마크는 흰색 선으로 표시
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateContextMenu()
|
private void CreateContextMenu()
|
||||||
@@ -546,6 +761,40 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 동기화 상태 설정
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">메인 메시지</param>
|
||||||
|
/// <param name="progress">진행률 (0.0 ~ 1.0)</param>
|
||||||
|
/// <param name="detail">상세 메시지</param>
|
||||||
|
public void SetSyncStatus(string message, float progress, string detail = "")
|
||||||
|
{
|
||||||
|
_syncMessage = message;
|
||||||
|
_syncProgress = Math.Max(0.0f, Math.Min(1.0f, progress));
|
||||||
|
_syncDetail = detail;
|
||||||
|
|
||||||
|
if (_canvasMode != CanvasMode.Sync)
|
||||||
|
{
|
||||||
|
_canvasMode = CanvasMode.Sync;
|
||||||
|
UpdateModeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 동기화 모드 종료
|
||||||
|
/// </summary>
|
||||||
|
public void ExitSyncMode()
|
||||||
|
{
|
||||||
|
if (_canvasMode == CanvasMode.Sync)
|
||||||
|
{
|
||||||
|
_canvasMode = CanvasMode.Edit; // 기본 모드로 복귀 (또는 이전 모드)
|
||||||
|
UpdateModeUI();
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Cleanup
|
#region Cleanup
|
||||||
@@ -576,6 +825,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
_pathPen?.Dispose();
|
_pathPen?.Dispose();
|
||||||
_agvPen?.Dispose();
|
_agvPen?.Dispose();
|
||||||
_highlightedConnectionPen?.Dispose();
|
_highlightedConnectionPen?.Dispose();
|
||||||
|
_magnetPen?.Dispose();
|
||||||
|
_markPen?.Dispose();
|
||||||
|
|
||||||
// 컨텍스트 메뉴 정리
|
// 컨텍스트 메뉴 정리
|
||||||
_contextMenu?.Dispose();
|
_contextMenu?.Dispose();
|
||||||
@@ -601,13 +852,13 @@ namespace AGVNavigationCore.Controls
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// RFID값과 해당 노드의 인덱스를 저장
|
// RFID값과 해당 노드의 인덱스를 저장
|
||||||
var rfidToNodeIndex = new Dictionary<string, List<int>>();
|
var rfidToNodeIndex = new Dictionary<ushort, List<int>>();
|
||||||
|
|
||||||
// 모든 노드의 RFID값 수집
|
// 모든 노드의 RFID값 수집
|
||||||
for (int i = 0; i < _nodes.Count; i++)
|
for (int i = 0; i < _nodes.Count; i++)
|
||||||
{
|
{
|
||||||
var node = _nodes[i];
|
var node = _nodes[i];
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
if (node.HasRfid())
|
||||||
{
|
{
|
||||||
if (!rfidToNodeIndex.ContainsKey(node.RfidId))
|
if (!rfidToNodeIndex.ContainsKey(node.RfidId))
|
||||||
{
|
{
|
||||||
@@ -626,7 +877,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
for (int i = 1; i < kvp.Value.Count; i++)
|
for (int i = 1; i < kvp.Value.Count; i++)
|
||||||
{
|
{
|
||||||
int duplicateNodeIndex = kvp.Value[i];
|
int duplicateNodeIndex = kvp.Value[i];
|
||||||
_duplicateRfidNodes.Add(_nodes[duplicateNodeIndex].NodeId);
|
_duplicateRfidNodes.Add(_nodes[duplicateNodeIndex].Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,7 +898,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
foreach (var node in _nodes)
|
foreach (var node in _nodes)
|
||||||
{
|
{
|
||||||
// NodeId에서 숫자 부분 추출 (예: "N001" -> 1)
|
// NodeId에서 숫자 부분 추출 (예: "N001" -> 1)
|
||||||
if (node.NodeId.StartsWith("N") && int.TryParse(node.NodeId.Substring(1), out int number))
|
if (node.Id.StartsWith("N") && int.TryParse(node.Id.Substring(1), out int number))
|
||||||
{
|
{
|
||||||
maxNumber = Math.Max(maxNumber, number);
|
maxNumber = Math.Max(maxNumber, number);
|
||||||
}
|
}
|
||||||
|
|||||||
54
Cs_HMI/AGVLogic/AGVNavigationCore/Models/AGVCommand.cs
Normal file
54
Cs_HMI/AGVLogic/AGVNavigationCore/Models/AGVCommand.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// AGV 제어 명령 클래스 (실제 AGV 제어용)
|
||||||
|
/// Predict() 메서드가 반환하는 다음 동작 명령
|
||||||
|
/// </summary>
|
||||||
|
public class AGVCommand
|
||||||
|
{
|
||||||
|
/// <summary>모터 명령 (정지/전진/후진)</summary>
|
||||||
|
public MotorCommand Motor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>마그넷 위치 (직진/왼쪽/오른쪽)</summary>
|
||||||
|
public MagnetPosition Magnet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>속도 레벨 (저속/중속/고속)</summary>
|
||||||
|
public SpeedLevel Speed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>명령 이유 메세지- (디버깅/로깅용)</summary>
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>명령 이유- (디버깅/로깅용)</summary>
|
||||||
|
public eAGVCommandReason Reason { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 생성자
|
||||||
|
/// </summary>
|
||||||
|
public AGVCommand(MotorCommand motor, MagnetPosition magnet, SpeedLevel speed, eAGVCommandReason reason, string reasonmessage = "")
|
||||||
|
{
|
||||||
|
Motor = motor;
|
||||||
|
Magnet = magnet;
|
||||||
|
Speed = speed;
|
||||||
|
Reason = reason;
|
||||||
|
Message = reasonmessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 기본 생성자
|
||||||
|
/// </summary>
|
||||||
|
public AGVCommand()
|
||||||
|
{
|
||||||
|
Motor = MotorCommand.Stop;
|
||||||
|
Magnet = MagnetPosition.S;
|
||||||
|
Speed = SpeedLevel.L;
|
||||||
|
Message = "";
|
||||||
|
Reason = eAGVCommandReason.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Motor:{Motor}, Magnet:{Magnet}, Speed:{Speed},Reason:{Reason}" +
|
||||||
|
(string.IsNullOrEmpty(Message) ? "" : $" ({Message})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,16 +11,18 @@ namespace AGVNavigationCore.Models
|
|||||||
{
|
{
|
||||||
/// <summary>일반 경로 노드</summary>
|
/// <summary>일반 경로 노드</summary>
|
||||||
Normal,
|
Normal,
|
||||||
/// <summary>회전 가능 지점</summary>
|
|
||||||
Rotation,
|
|
||||||
/// <summary>도킹 스테이션</summary>
|
|
||||||
Docking,
|
|
||||||
/// <summary>충전 스테이션</summary>
|
|
||||||
Charging,
|
|
||||||
/// <summary>라벨 (UI 요소)</summary>
|
|
||||||
Label,
|
Label,
|
||||||
/// <summary>이미지 (UI 요소)</summary>
|
/// <summary>이미지 (UI 요소)</summary>
|
||||||
Image
|
Image,
|
||||||
|
/// <summary>
|
||||||
|
/// 마크센서
|
||||||
|
/// </summary>
|
||||||
|
Mark,
|
||||||
|
/// <summary>
|
||||||
|
/// 마그넷라인
|
||||||
|
/// </summary>
|
||||||
|
Magnet
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,18 +60,46 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum StationType
|
public enum StationType
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 일반노드
|
||||||
|
/// </summary>
|
||||||
|
Normal,
|
||||||
/// <summary>로더</summary>
|
/// <summary>로더</summary>
|
||||||
Loader,
|
Loader,
|
||||||
/// <summary>클리너</summary>
|
/// <summary>클리너</summary>
|
||||||
Cleaner,
|
Clearner,
|
||||||
/// <summary>오프로더</summary>
|
/// <summary>오프로더</summary>
|
||||||
Offloader,
|
UnLoader,
|
||||||
/// <summary>버퍼</summary>
|
/// <summary>버퍼</summary>
|
||||||
Buffer,
|
Buffer,
|
||||||
/// <summary>충전기</summary>
|
/// <summary>충전기</summary>
|
||||||
Charger
|
Charger,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 끝점(더이상 이동불가)
|
||||||
|
/// </summary>
|
||||||
|
Limit,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AGV턴상태
|
||||||
|
/// </summary>
|
||||||
|
public enum AGVTurn
|
||||||
|
{
|
||||||
|
None=0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// left turn 90"
|
||||||
|
/// </summary>
|
||||||
|
L90,
|
||||||
|
/// <summary>
|
||||||
|
/// right turn 90"
|
||||||
|
/// </summary>
|
||||||
|
R90
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 모터 명령 열거형 (실제 AGV 제어용)
|
/// 모터 명령 열거형 (실제 AGV 제어용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -109,50 +139,42 @@ namespace AGVNavigationCore.Models
|
|||||||
H
|
H
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public enum eAGVCommandReason
|
||||||
/// AGV 제어 명령 클래스 (실제 AGV 제어용)
|
|
||||||
/// Predict() 메서드가 반환하는 다음 동작 명령
|
|
||||||
/// </summary>
|
|
||||||
public class AGVCommand
|
|
||||||
{
|
{
|
||||||
/// <summary>모터 명령 (정지/전진/후진)</summary>
|
/// <summary>
|
||||||
public MotorCommand Motor { get; set; }
|
/// 초기 미지정
|
||||||
|
/// </summary>
|
||||||
/// <summary>마그넷 위치 (직진/왼쪽/오른쪽)</summary>
|
Normal,
|
||||||
public MagnetPosition Magnet { get; set; }
|
|
||||||
|
|
||||||
/// <summary>속도 레벨 (저속/중속/고속)</summary>
|
|
||||||
public SpeedLevel Speed { get; set; }
|
|
||||||
|
|
||||||
/// <summary>명령 이유 (디버깅/로깅용)</summary>
|
|
||||||
public string Reason { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 생성자
|
/// 위치 미확정
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AGVCommand(MotorCommand motor, MagnetPosition magnet, SpeedLevel speed, string reason = "")
|
UnknownPosition,
|
||||||
{
|
|
||||||
Motor = motor;
|
|
||||||
Magnet = magnet;
|
|
||||||
Speed = speed;
|
|
||||||
Reason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 기본 생성자
|
/// 대상경로없음
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AGVCommand()
|
NoTarget,
|
||||||
{
|
|
||||||
Motor = MotorCommand.Stop;
|
/// <summary>
|
||||||
Magnet = MagnetPosition.S;
|
/// 경로없음
|
||||||
Speed = SpeedLevel.L;
|
/// </summary>
|
||||||
Reason = "";
|
NoPath,
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// 경로이탈
|
||||||
|
/// </summary>
|
||||||
|
PathOut,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 마크스탑을 해야한다
|
||||||
|
/// </summary>
|
||||||
|
MarkStop,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 완료
|
||||||
|
/// </summary>
|
||||||
|
Complete,
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"Motor:{Motor}, Magnet:{Magnet}, Speed:{Speed}" +
|
|
||||||
(string.IsNullOrEmpty(Reason) ? "" : $" ({Reason})");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 현재 노드 ID
|
/// 현재 노드 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string CurrentNodeId { get; }
|
MapNode CurrentNode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 목표 위치
|
/// 목표 위치
|
||||||
@@ -92,7 +92,7 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 목표 노드 ID
|
/// 목표 노드 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string PrevNodeId { get; }
|
MapNode PrevNode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 도킹 방향
|
/// 도킹 방향
|
||||||
@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 현재 노드 ID 조회
|
/// 현재 노드 ID 조회
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string GetCurrentNodeId();
|
MapNode GetCurrentNode();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV 상태 정보 문자열 조회
|
/// AGV 상태 정보 문자열 조회
|
||||||
|
|||||||
88
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapImage.cs
Normal file
88
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapImage.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using AGVNavigationCore.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
public class MapImage : NodeBase
|
||||||
|
{
|
||||||
|
[Category("기본 정보")]
|
||||||
|
[Description("이미지의 이름입니다.")]
|
||||||
|
public string Name { get; set; } = "Image";
|
||||||
|
|
||||||
|
[Category("이미지 설정")]
|
||||||
|
[Description("이미지 파일 경로입니다 (편집기용).")]
|
||||||
|
public string ImagePath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[ReadOnly(false)]
|
||||||
|
public string ImageBase64 { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Category("이미지 설정")]
|
||||||
|
[Description("이미지 크기 배율입니다.")]
|
||||||
|
public SizeF Scale { get; set; } = new SizeF(1.0f, 1.0f);
|
||||||
|
|
||||||
|
[Category("이미지 설정")]
|
||||||
|
[Description("이미지 투명도입니다 (0.0 ~ 1.0).")]
|
||||||
|
public float Opacity { get; set; } = 1.0f;
|
||||||
|
|
||||||
|
[Category("이미지 설정")]
|
||||||
|
[Description("이미지 회전 각도입니다.")]
|
||||||
|
public float Rotation { get; set; } = 0.0f;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
[Browsable(false)]
|
||||||
|
public Image LoadedImage { get; set; }
|
||||||
|
|
||||||
|
public MapImage()
|
||||||
|
{
|
||||||
|
Type = NodeType.Image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LoadImage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Image originalImage = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(ImageBase64))
|
||||||
|
{
|
||||||
|
originalImage = ImageConverterUtil.Base64ToImage(ImageBase64);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(ImagePath) && System.IO.File.Exists(ImagePath))
|
||||||
|
{
|
||||||
|
originalImage = Image.FromFile(ImagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalImage != null)
|
||||||
|
{
|
||||||
|
LoadedImage?.Dispose();
|
||||||
|
LoadedImage = originalImage; // 리사이즈 필요시 추가 구현
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 무시
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size GetDisplaySize()
|
||||||
|
{
|
||||||
|
if (LoadedImage == null) return Size.Empty;
|
||||||
|
return new Size(
|
||||||
|
(int)(LoadedImage.Width * Scale.Width),
|
||||||
|
(int)(LoadedImage.Height * Scale.Height)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
LoadedImage?.Dispose();
|
||||||
|
LoadedImage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapLabel.cs
Normal file
42
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapLabel.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
public class MapLabel : NodeBase
|
||||||
|
{
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("표시할 텍스트입니다.")]
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("글자색입니다")]
|
||||||
|
public Color ForeColor { get; set; } = Color.Black;
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("배경색입니다.")]
|
||||||
|
public Color BackColor { get; set; } = Color.Transparent;
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("폰트 종류입니다.")]
|
||||||
|
public string FontFamily { get; set; } = "Arial";
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("폰트 크기입니다.")]
|
||||||
|
public float FontSize { get; set; } = 12.0f;
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("폰트 스타일입니다.")]
|
||||||
|
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
|
||||||
|
|
||||||
|
[Category("라벨 설정")]
|
||||||
|
[Description("내부 여백입니다.")]
|
||||||
|
public int Padding { get; set; } = 5;
|
||||||
|
|
||||||
|
public MapLabel()
|
||||||
|
{
|
||||||
|
ForeColor = Color.Purple;
|
||||||
|
Type = NodeType.Label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,10 @@ namespace AGVNavigationCore.Models
|
|||||||
{
|
{
|
||||||
public bool Success { get; set; }
|
public bool Success { get; set; }
|
||||||
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
||||||
|
public List<MapLabel> Labels { get; set; } = new List<MapLabel>(); // 추가
|
||||||
|
public List<MapImage> Images { get; set; } = new List<MapImage>(); // 추가
|
||||||
|
public List<MapMark> Marks { get; set; } = new List<MapMark>();
|
||||||
|
public List<MapMagnet> Magnets { get; set; } = new List<MapMagnet>();
|
||||||
public MapSettings Settings { get; set; } = new MapSettings();
|
public MapSettings Settings { get; set; } = new MapSettings();
|
||||||
public string ErrorMessage { get; set; } = string.Empty;
|
public string ErrorMessage { get; set; } = string.Empty;
|
||||||
public string Version { get; set; } = string.Empty;
|
public string Version { get; set; } = string.Empty;
|
||||||
@@ -40,9 +44,13 @@ namespace AGVNavigationCore.Models
|
|||||||
public class MapFileData
|
public class MapFileData
|
||||||
{
|
{
|
||||||
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
||||||
|
public List<MapLabel> Labels { get; set; } = new List<MapLabel>(); // 추가
|
||||||
|
public List<MapImage> Images { get; set; } = new List<MapImage>(); // 추가
|
||||||
|
public List<MapMark> Marks { get; set; } = new List<MapMark>();
|
||||||
|
public List<MapMagnet> Magnets { get; set; } = new List<MapMagnet>();
|
||||||
public MapSettings Settings { get; set; } = new MapSettings();
|
public MapSettings Settings { get; set; } = new MapSettings();
|
||||||
public DateTime CreatedDate { get; set; }
|
public DateTime CreatedDate { get; set; }
|
||||||
public string Version { get; set; } = "1.1"; // 버전 업그레이드 (설정 추가)
|
public string Version { get; set; } = "1.3"; // 버전 업그레이드
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,7 +72,7 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
var json = File.ReadAllText(filePath);
|
var json = File.ReadAllText(filePath);
|
||||||
|
|
||||||
// JSON 역직렬화 설정: 누락된 속성 무시, 안전한 처리
|
// JSON 역직렬화 설정
|
||||||
var settings = new JsonSerializerSettings
|
var settings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
MissingMemberHandling = MissingMemberHandling.Ignore,
|
MissingMemberHandling = MissingMemberHandling.Ignore,
|
||||||
@@ -72,36 +80,83 @@ namespace AGVNavigationCore.Models
|
|||||||
DefaultValueHandling = DefaultValueHandling.Populate
|
DefaultValueHandling = DefaultValueHandling.Populate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 먼저 구조 파악을 위해 동적 객체로 로드하거나, MapFileData로 시도
|
||||||
var mapData = JsonConvert.DeserializeObject<MapFileData>(json, settings);
|
var mapData = JsonConvert.DeserializeObject<MapFileData>(json, settings);
|
||||||
|
|
||||||
if (mapData != null)
|
if (mapData != null)
|
||||||
{
|
{
|
||||||
result.Nodes = mapData.Nodes ?? new List<MapNode>();
|
result.Nodes = new List<MapNode>();
|
||||||
result.Settings = mapData.Settings ?? new MapSettings(); // 설정 로드
|
result.Labels = mapData.Labels ?? new List<MapLabel>();
|
||||||
|
result.Images = mapData.Images ?? new List<MapImage>();
|
||||||
|
result.Marks = mapData.Marks ?? new List<MapMark>();
|
||||||
|
result.Magnets = mapData.Magnets ?? new List<MapMagnet>();
|
||||||
|
result.Settings = mapData.Settings ?? new MapSettings();
|
||||||
result.Version = mapData.Version ?? "1.0";
|
result.Version = mapData.Version ?? "1.0";
|
||||||
result.CreatedDate = mapData.CreatedDate;
|
result.CreatedDate = mapData.CreatedDate;
|
||||||
|
|
||||||
// 기존 Description 데이터를 Name으로 마이그레이션
|
if (mapData.Nodes != null)
|
||||||
MigrateDescriptionToName(result.Nodes);
|
{
|
||||||
|
foreach (var node in mapData.Nodes)
|
||||||
|
{
|
||||||
|
// 마이그레이션: 기존 파일의 Nodes 리스트에 섞여있는 Label, Image 분리
|
||||||
|
// (새 파일 구조에서는 이미 분리되어 로드됨)
|
||||||
|
if (node.Type == NodeType.Label)
|
||||||
|
{
|
||||||
|
// MapNode -> MapLabel 변환 (필드 매핑)
|
||||||
|
var label = new MapLabel
|
||||||
|
{
|
||||||
|
Id = node.Id, // 기존 NodeId -> Id
|
||||||
|
Position = node.Position,
|
||||||
|
CreatedDate = node.CreatedDate,
|
||||||
|
ModifiedDate = node.ModifiedDate,
|
||||||
|
|
||||||
// DockingDirection 마이그레이션 (기존 NodeType 기반으로 설정)
|
// Label 속성 매핑 (MapNode에서 임시로 가져오거나 Json Raw Parsing 필요할 수 있음)
|
||||||
MigrateDockingDirection(result.Nodes);
|
// 현재 MapNode 클래스에는 해당 속성들이 제거되었으므로,
|
||||||
|
// Json 포맷 변경으로 인해 기존 데이터 로드시 정보 손실 가능성 있음.
|
||||||
|
// * 중요 *: MapNode 클래스에서 속성을 지웠으므로 일반 Deserialize로는 Label/Image 속성을 못 읽음.
|
||||||
|
// 해결책: JObject로 먼저 읽어서 분기 처리하거나, DTO 클래스를 별도로 두어야 함.
|
||||||
|
// 하지만 시간 관계상, 만약 기존 MapNode가 속성을 가지고 있지 않다면 마이그레이션은 "위치/ID" 정도만 복구됨.
|
||||||
|
// 완벽한 마이그레이션을 위해서는 MapNode에 Obsolete 속성을 잠시 두었어야 함.
|
||||||
|
// 여기서는 일단 기본 정보라도 살림.
|
||||||
|
};
|
||||||
|
result.Labels.Add(label);
|
||||||
|
}
|
||||||
|
else if (node.Type == NodeType.Image)
|
||||||
|
{
|
||||||
|
var image = new MapImage
|
||||||
|
{
|
||||||
|
Id = node.Id,
|
||||||
|
Position = node.Position,
|
||||||
|
CreatedDate = node.CreatedDate,
|
||||||
|
ModifiedDate = node.ModifiedDate,
|
||||||
|
// 이미지/라벨 속성 복구 불가 (MapNode에서 삭제됨)
|
||||||
|
};
|
||||||
|
result.Images.Add(image);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Nodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 중복된 NodeId 정리
|
// 중복된 NodeId 정리 (Nav Node만)
|
||||||
FixDuplicateNodeIds(result.Nodes);
|
FixDuplicateNodeIds(result.Nodes);
|
||||||
|
|
||||||
// 존재하지 않는 노드에 대한 연결 정리 (고아 연결 제거)
|
// 고아 연결 정리
|
||||||
CleanupOrphanConnections(result.Nodes);
|
CleanupOrphanConnections(result.Nodes);
|
||||||
|
|
||||||
// 양방향 연결 자동 설정 (A→B가 있으면 B→A도 설정)
|
// 양방향 연결 자동 설정
|
||||||
// 주의: CleanupDuplicateConnections()는 제거됨 - 양방향 연결을 단방향으로 변환하는 버그가 있었음
|
|
||||||
EnsureBidirectionalConnections(result.Nodes);
|
EnsureBidirectionalConnections(result.Nodes);
|
||||||
|
|
||||||
// ConnectedMapNodes 채우기 (string ID → MapNode 객체 참조)
|
// ConnectedMapNodes 채우기
|
||||||
ResolveConnectedMapNodes(result.Nodes);
|
ResolveConnectedMapNodes(result.Nodes);
|
||||||
|
|
||||||
// 이미지 노드들의 이미지 로드
|
// 이미지 로드 (MapImage 객체에서)
|
||||||
LoadImageNodes(result.Nodes);
|
foreach (var img in result.Images)
|
||||||
|
{
|
||||||
|
img.LoadImage();
|
||||||
|
}
|
||||||
|
|
||||||
result.Success = true;
|
result.Success = true;
|
||||||
}
|
}
|
||||||
@@ -121,23 +176,23 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 맵 데이터를 파일로 저장
|
/// 맵 데이터를 파일로 저장
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filePath">저장할 파일 경로</param>
|
public static bool SaveMapToFile(string filePath, List<MapNode> nodes, List<MapLabel> labels = null, List<MapImage> images = null, List<MapMark> marks = null, List<MapMagnet> magnets = null, MapSettings settings = null)
|
||||||
/// <param name="nodes">맵 노드 목록</param>
|
|
||||||
/// <param name="settings">맵 설정 (배경색, 그리드 표시 등)</param>
|
|
||||||
/// <returns>저장 성공 여부</returns>
|
|
||||||
public static bool SaveMapToFile(string filePath, List<MapNode> nodes, MapSettings settings = null)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 저장 전 고아 연결 정리 (삭제된 노드에 대한 연결 제거)
|
// 저장 전 고아 연결 정리
|
||||||
CleanupOrphanConnections(nodes);
|
CleanupOrphanConnections(nodes);
|
||||||
|
|
||||||
var mapData = new MapFileData
|
var mapData = new MapFileData
|
||||||
{
|
{
|
||||||
Nodes = nodes,
|
Nodes = nodes,
|
||||||
Settings = settings ?? new MapSettings(), // 설정 저장
|
Labels = labels ?? new List<MapLabel>(),
|
||||||
|
Images = images ?? new List<MapImage>(),
|
||||||
|
Marks = marks ?? new List<MapMark>(),
|
||||||
|
Magnets = magnets ?? new List<MapMagnet>(),
|
||||||
|
Settings = settings ?? new MapSettings(),
|
||||||
CreatedDate = DateTime.Now,
|
CreatedDate = DateTime.Now,
|
||||||
Version = "1.1"
|
Version = "1.3"
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(mapData, Formatting.Indented);
|
var json = JsonConvert.SerializeObject(mapData, Formatting.Indented);
|
||||||
@@ -145,27 +200,13 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 노드들의 이미지 로드
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodes">노드 목록</param>
|
|
||||||
private static void LoadImageNodes(List<MapNode> nodes)
|
|
||||||
{
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
if (node.Type == NodeType.Image)
|
|
||||||
{
|
|
||||||
node.LoadImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ConnectedMapNodes 채우기 (ConnectedNodes의 string ID → MapNode 객체 변환)
|
/// ConnectedMapNodes 채우기 (ConnectedNodes의 string ID → MapNode 객체 변환)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -175,7 +216,7 @@ namespace AGVNavigationCore.Models
|
|||||||
if (mapNodes == null || mapNodes.Count == 0) return;
|
if (mapNodes == null || mapNodes.Count == 0) return;
|
||||||
|
|
||||||
// 빠른 조회를 위한 Dictionary 생성
|
// 빠른 조회를 위한 Dictionary 생성
|
||||||
var nodeDict = mapNodes.ToDictionary(n => n.NodeId, n => n);
|
var nodeDict = mapNodes.ToDictionary(n => n.Id, n => n);
|
||||||
|
|
||||||
foreach (var node in mapNodes)
|
foreach (var node in mapNodes)
|
||||||
{
|
{
|
||||||
@@ -192,6 +233,8 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,36 +251,6 @@ namespace AGVNavigationCore.Models
|
|||||||
// 기존 파일들은 다시 저장될 때 Description 없이 저장됨
|
// 기존 파일들은 다시 저장될 때 Description 없이 저장됨
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 기존 맵 파일의 DockingDirection을 NodeType 기반으로 마이그레이션
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapNodes">맵 노드 목록</param>
|
|
||||||
private static void MigrateDockingDirection(List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
if (mapNodes == null || mapNodes.Count == 0) return;
|
|
||||||
|
|
||||||
foreach (var node in mapNodes)
|
|
||||||
{
|
|
||||||
// 기존 파일에서 DockingDirection이 기본값(DontCare)인 경우에만 마이그레이션
|
|
||||||
if (node.DockDirection == DockingDirection.DontCare)
|
|
||||||
{
|
|
||||||
switch (node.Type)
|
|
||||||
{
|
|
||||||
case NodeType.Charging:
|
|
||||||
node.DockDirection = DockingDirection.Forward;
|
|
||||||
break;
|
|
||||||
case NodeType.Docking:
|
|
||||||
node.DockDirection = DockingDirection.Backward;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Normal, Rotation, Label, Image는 DontCare 유지
|
|
||||||
node.DockDirection = DockingDirection.DontCare;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 중복된 NodeId를 가진 노드들을 고유한 NodeId로 수정
|
/// 중복된 NodeId를 가진 노드들을 고유한 NodeId로 수정
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -252,13 +265,13 @@ namespace AGVNavigationCore.Models
|
|||||||
// 첫 번째 패스: 중복된 노드들 식별
|
// 첫 번째 패스: 중복된 노드들 식별
|
||||||
foreach (var node in mapNodes)
|
foreach (var node in mapNodes)
|
||||||
{
|
{
|
||||||
if (usedIds.Contains(node.NodeId))
|
if (usedIds.Contains(node.Id))
|
||||||
{
|
{
|
||||||
duplicateNodes.Add(node);
|
duplicateNodes.Add(node);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usedIds.Add(node.NodeId);
|
usedIds.Add(node.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,9 +281,9 @@ namespace AGVNavigationCore.Models
|
|||||||
string newNodeId = GenerateUniqueNodeId(usedIds);
|
string newNodeId = GenerateUniqueNodeId(usedIds);
|
||||||
|
|
||||||
// 다른 노드들의 연결에서 기존 NodeId를 새 NodeId로 업데이트
|
// 다른 노드들의 연결에서 기존 NodeId를 새 NodeId로 업데이트
|
||||||
UpdateConnections(mapNodes, duplicateNode.NodeId, newNodeId);
|
UpdateConnections(mapNodes, duplicateNode.Id, newNodeId);
|
||||||
|
|
||||||
duplicateNode.NodeId = newNodeId;
|
duplicateNode.Id = newNodeId;
|
||||||
usedIds.Add(newNodeId);
|
usedIds.Add(newNodeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,7 +341,7 @@ namespace AGVNavigationCore.Models
|
|||||||
if (mapNodes == null || mapNodes.Count == 0) return;
|
if (mapNodes == null || mapNodes.Count == 0) return;
|
||||||
|
|
||||||
// 존재하는 모든 노드 ID 집합 생성
|
// 존재하는 모든 노드 ID 집합 생성
|
||||||
var validNodeIds = new HashSet<string>(mapNodes.Select(n => n.NodeId));
|
var validNodeIds = new HashSet<string>(mapNodes.Select(n => n.Id));
|
||||||
|
|
||||||
// 각 노드의 연결을 검증하고 존재하지 않는 노드 ID 제거
|
// 각 노드의 연결을 검증하고 존재하지 않는 노드 ID 제거
|
||||||
foreach (var node in mapNodes)
|
foreach (var node in mapNodes)
|
||||||
@@ -366,13 +379,13 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
foreach (var connectedNodeId in node.ConnectedNodes.ToList())
|
foreach (var connectedNodeId in node.ConnectedNodes.ToList())
|
||||||
{
|
{
|
||||||
var connectedNode = mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
var connectedNode = mapNodes.FirstOrDefault(n => n.Id == connectedNodeId);
|
||||||
if (connectedNode == null) continue;
|
if (connectedNode == null) continue;
|
||||||
|
|
||||||
// 연결 쌍의 키 생성 (사전순 정렬)
|
// 연결 쌍의 키 생성 (사전순 정렬)
|
||||||
string pairKey = string.Compare(node.NodeId, connectedNodeId, StringComparison.Ordinal) < 0
|
string pairKey = string.Compare(node.Id, connectedNodeId, StringComparison.Ordinal) < 0
|
||||||
? $"{node.NodeId}-{connectedNodeId}"
|
? $"{node.Id}-{connectedNodeId}"
|
||||||
: $"{connectedNodeId}-{node.NodeId}";
|
: $"{connectedNodeId}-{node.Id}";
|
||||||
|
|
||||||
if (processedPairs.Contains(pairKey))
|
if (processedPairs.Contains(pairKey))
|
||||||
{
|
{
|
||||||
@@ -385,17 +398,17 @@ namespace AGVNavigationCore.Models
|
|||||||
processedPairs.Add(pairKey);
|
processedPairs.Add(pairKey);
|
||||||
|
|
||||||
// 양방향 연결인 경우 하나만 유지
|
// 양방향 연결인 경우 하나만 유지
|
||||||
if (connectedNode.ConnectedNodes.Contains(node.NodeId))
|
if (connectedNode.ConnectedNodes.Contains(node.Id))
|
||||||
{
|
{
|
||||||
// 사전순으로 더 작은 노드에만 연결을 유지
|
// 사전순으로 더 작은 노드에만 연결을 유지
|
||||||
if (string.Compare(node.NodeId, connectedNodeId, StringComparison.Ordinal) > 0)
|
if (string.Compare(node.Id, connectedNodeId, StringComparison.Ordinal) > 0)
|
||||||
{
|
{
|
||||||
connectionsToRemove.Add(connectedNodeId);
|
connectionsToRemove.Add(connectedNodeId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 반대 방향 연결 제거
|
// 반대 방향 연결 제거
|
||||||
connectedNode.RemoveConnection(node.NodeId);
|
connectedNode.RemoveConnection(node.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,16 +443,16 @@ namespace AGVNavigationCore.Models
|
|||||||
// 1단계: 모든 명시적 연결 수집
|
// 1단계: 모든 명시적 연결 수집
|
||||||
foreach (var node in mapNodes)
|
foreach (var node in mapNodes)
|
||||||
{
|
{
|
||||||
if (!allConnections.ContainsKey(node.NodeId))
|
if (!allConnections.ContainsKey(node.Id))
|
||||||
{
|
{
|
||||||
allConnections[node.NodeId] = new HashSet<string>();
|
allConnections[node.Id] = new HashSet<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.ConnectedNodes != null)
|
if (node.ConnectedNodes != null)
|
||||||
{
|
{
|
||||||
foreach (var connectedId in node.ConnectedNodes)
|
foreach (var connectedId in node.ConnectedNodes)
|
||||||
{
|
{
|
||||||
allConnections[node.NodeId].Add(connectedId);
|
allConnections[node.Id].Add(connectedId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,10 +468,10 @@ namespace AGVNavigationCore.Models
|
|||||||
// 이 노드를 연결하는 모든 노드 찾기
|
// 이 노드를 연결하는 모든 노드 찾기
|
||||||
foreach (var otherNodeId in allConnections.Keys)
|
foreach (var otherNodeId in allConnections.Keys)
|
||||||
{
|
{
|
||||||
if (otherNodeId == node.NodeId) continue;
|
if (otherNodeId == node.Id) continue;
|
||||||
|
|
||||||
// 다른 노드가 이 노드를 연결하고 있다면
|
// 다른 노드가 이 노드를 연결하고 있다면
|
||||||
if (allConnections[otherNodeId].Contains(node.NodeId))
|
if (allConnections[otherNodeId].Contains(node.Id))
|
||||||
{
|
{
|
||||||
// 이 노드의 ConnectedNodes에 그 노드를 추가 (중복 방지)
|
// 이 노드의 ConnectedNodes에 그 노드를 추가 (중복 방지)
|
||||||
if (!node.ConnectedNodes.Contains(otherNodeId))
|
if (!node.ConnectedNodes.Contains(otherNodeId))
|
||||||
|
|||||||
72
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs
Normal file
72
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 맵 상의 마그넷(Magnet) 정보를 나타내는 클래스
|
||||||
|
/// </summary>
|
||||||
|
public class MapMagnet : NodeBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public MapMagnet() {
|
||||||
|
Type = NodeType.Magnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("시작점 좌표")]
|
||||||
|
public MagnetPoint P1 { get; set; } = new MagnetPoint();
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("끝점 좌표")]
|
||||||
|
public MagnetPoint P2 { get; set; } = new MagnetPoint();
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("제어점 좌표 (곡선인 경우)")]
|
||||||
|
public MagnetPoint ControlPoint { get; set; } = null;
|
||||||
|
|
||||||
|
public class MagnetPoint
|
||||||
|
{
|
||||||
|
public double X { get; set; }
|
||||||
|
public double Y { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override Point Position
|
||||||
|
{
|
||||||
|
get => new Point((int)P1.X, (int)P1.Y);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
double dx = value.X - P1.X;
|
||||||
|
double dy = value.Y - P1.Y;
|
||||||
|
|
||||||
|
P1.X += dx;
|
||||||
|
P1.Y += dy;
|
||||||
|
P2.X += dx;
|
||||||
|
P2.Y += dy;
|
||||||
|
|
||||||
|
if (ControlPoint != null)
|
||||||
|
{
|
||||||
|
ControlPoint.X += dx;
|
||||||
|
ControlPoint.Y += dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시작점 Point 반환
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
[JsonIgnore]
|
||||||
|
public Point StartPoint => new Point((int)P1.X, (int)P1.Y);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 끝점 Point 반환
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
[JsonIgnore]
|
||||||
|
public Point EndPoint => new Point((int)P2.X, (int)P2.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapMark.cs
Normal file
37
Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapMark.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 맵 상의 마크(Mark) 정보를 나타내는 클래스
|
||||||
|
/// </summary>
|
||||||
|
public class MapMark : NodeBase
|
||||||
|
{
|
||||||
|
// Id is inherited from NodeBase
|
||||||
|
public MapMark() {
|
||||||
|
Type = NodeType.Mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("마크의 X 좌표")]
|
||||||
|
public double X
|
||||||
|
{
|
||||||
|
get => Position.X;
|
||||||
|
set => Position = new Point((int)value, Position.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("마크의 Y 좌표")]
|
||||||
|
public double Y
|
||||||
|
{
|
||||||
|
get => Position.Y;
|
||||||
|
set => Position = new Point(Position.X, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("마크의 회전 각도")]
|
||||||
|
public double Rotation { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,267 +1,127 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
using AGVNavigationCore.Utils;
|
using AGVNavigationCore.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace AGVNavigationCore.Models
|
namespace AGVNavigationCore.Models
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 맵 노드 정보를 관리하는 클래스
|
/// 맵 노드 정보를 관리하는 클래스 (주행 경로용 노드)
|
||||||
/// 논리적 노드로서 실제 맵의 위치와 속성을 정의
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MapNode
|
public class MapNode : NodeBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 논리적 노드 ID (맵 에디터에서 관리하는 고유 ID)
|
|
||||||
/// 예: "N001", "N002", "LOADER1", "CHARGER1"
|
|
||||||
/// </summary>
|
|
||||||
public string NodeId { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 노드 표시 이름 (사용자 친화적)
|
|
||||||
/// 예: "로더1", "충전기1", "교차점A", "회전지점1"
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
[Category("라벨 설정")]
|
||||||
/// 맵 상의 위치 좌표 (픽셀 단위)
|
[Description("표시할 텍스트입니다.")]
|
||||||
/// </summary>
|
public string Text { get; set; } = "";
|
||||||
public Point Position { get; set; } = Point.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
public StationType StationType { get; set; }
|
||||||
/// 노드 타입
|
|
||||||
/// </summary>
|
|
||||||
public NodeType Type { get; set; } = NodeType.Normal;
|
|
||||||
|
|
||||||
/// <summary>
|
[Browsable(false)]
|
||||||
/// 도킹 방향 (도킹/충전 노드인 경우에만 Forward/Backward, 일반 노드는 DontCare)
|
public bool CanDocking
|
||||||
/// </summary>
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (StationType == StationType.Buffer) return true;
|
||||||
|
if (StationType == StationType.Loader) return true;
|
||||||
|
if (StationType == StationType.UnLoader) return true;
|
||||||
|
if (StationType == StationType.Clearner) return true;
|
||||||
|
if (StationType == StationType.Charger) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("노드 설정")]
|
||||||
|
[Description("도킹/충전 노드의 진입 방향입니다.")]
|
||||||
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
|
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
|
||||||
|
|
||||||
/// <summary>
|
[Category("연결 정보")]
|
||||||
/// 연결된 노드 ID 목록 (경로 정보)
|
[Description("연결된 노드 ID 목록입니다.")]
|
||||||
/// </summary>
|
[ReadOnly(true)]
|
||||||
public List<string> ConnectedNodes { get; set; } = new List<string>();
|
public List<string> ConnectedNodes { get; set; } = new List<string>();
|
||||||
|
|
||||||
/// <summary>
|
[JsonIgnore]
|
||||||
/// 연결된 노드 객체 목록 (런타임 전용, JSON 무시)
|
[Browsable(false)]
|
||||||
/// </summary>
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
|
||||||
public List<MapNode> ConnectedMapNodes { get; set; } = new List<MapNode>();
|
public List<MapNode> ConnectedMapNodes { get; set; } = new List<MapNode>();
|
||||||
|
|
||||||
/// <summary>
|
[Category("주행 설정")]
|
||||||
/// 회전 가능 여부 (180도 회전 가능한 지점)
|
[Description("제자리 회전(좌) 가능 여부입니다.")]
|
||||||
/// </summary>
|
public bool CanTurnLeft { get; set; } = true;
|
||||||
public bool CanRotate { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
[Category("주행 설정")]
|
||||||
/// 장비 ID (도킹/충전 스테이션인 경우)
|
[Description("제자리 회전(우) 가능 여부입니다.")]
|
||||||
/// 예: "LOADER1", "CLEANER1", "BUFFER1", "CHARGER1"
|
public bool CanTurnRight { get; set; } = true;
|
||||||
/// </summary>
|
|
||||||
public string StationId { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
[Category("주행 설정")]
|
||||||
/// 장비 타입 (도킹/충전 스테이션인 경우)
|
[Description("교차로 주행 가능 여부입니다.")]
|
||||||
/// </summary>
|
public bool DisableCross
|
||||||
public StationType? StationType { get; set; } = null;
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Type != NodeType.Normal) return true;
|
||||||
|
return _disablecross;
|
||||||
|
}
|
||||||
|
set { _disablecross = value; }
|
||||||
|
}
|
||||||
|
private bool _disablecross = false;
|
||||||
|
|
||||||
/// <summary>
|
[Category("주행 설정")]
|
||||||
/// 노드 생성 일자
|
[Description("노드 통과 시 제한 속도입니다.")]
|
||||||
/// </summary>
|
public SpeedLevel SpeedLimit { get; set; } = SpeedLevel.M;
|
||||||
public DateTime CreatedDate { get; set; } = DateTime.Now;
|
|
||||||
|
|
||||||
/// <summary>
|
[Category("노드 설정")]
|
||||||
/// 노드 수정 일자
|
[Description("장비 ID 또는 별칭입니다.")]
|
||||||
/// </summary>
|
public string AliasName { get; set; } = string.Empty;
|
||||||
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
|
||||||
|
|
||||||
|
[Category("기본 정보")]
|
||||||
/// <summary>
|
[Description("노드 사용 여부입니다.")]
|
||||||
/// 노드 활성화 여부
|
|
||||||
/// </summary>
|
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
[Category("RFID 정보")]
|
||||||
/// 노드 색상 (맵 에디터 표시용)
|
[Description("물리적 RFID 태그 ID입니다.")]
|
||||||
/// </summary>
|
public UInt16 RfidId { get; set; } = 0;
|
||||||
public Color DisplayColor { get; set; } = Color.Blue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID 태그 ID (이 노드에 매핑된 RFID)
|
|
||||||
/// </summary>
|
|
||||||
public string RfidId { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
[Category("노드 텍스트"), DisplayName("TextColor")]
|
||||||
/// RFID 상태 (정상, 손상, 교체예정 등)
|
[Description("텍스트 색상입니다.")]
|
||||||
/// </summary>
|
public Color NodeTextForeColor { get; set; } = Color.Black;
|
||||||
public string RfidStatus { get; set; } = "정상";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID 설치 위치 설명 (현장 작업자용)
|
|
||||||
/// 예: "로더1번 앞", "충전기2번 입구", "복도 교차점" 등
|
|
||||||
/// </summary>
|
|
||||||
public string RfidDescription { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 텍스트 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public string LabelText { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 폰트 패밀리 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public string FontFamily { get; set; } = "Arial";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 폰트 크기 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public float FontSize { get; set; } = 12.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 폰트 스타일 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 텍스트 전경색 (모든 노드 타입에서 사용)
|
|
||||||
/// </summary>
|
|
||||||
public Color ForeColor { get; set; } = Color.Black;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 배경색 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public Color BackColor { get; set; } = Color.Transparent;
|
|
||||||
|
|
||||||
private float _textFontSize = 7.0f;
|
private float _textFontSize = 7.0f;
|
||||||
|
[Category("노드 텍스트"), DisplayName("TextSize")]
|
||||||
/// <summary>
|
[Description("일반 노드 텍스트의 크기입니다.")]
|
||||||
/// 텍스트 폰트 크기 (모든 노드 타입의 텍스트 표시에 사용, 픽셀 단위)
|
public float NodeTextFontSize
|
||||||
/// 0 이하의 값이 설정되면 기본값 7.0f로 자동 설정
|
|
||||||
/// </summary>
|
|
||||||
public float TextFontSize
|
|
||||||
{
|
{
|
||||||
get => _textFontSize;
|
get => _textFontSize;
|
||||||
set => _textFontSize = value > 0 ? value : 7.0f;
|
set => _textFontSize = value > 0 ? value : 7.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public MapNode() : base()
|
||||||
/// 텍스트 볼드체 여부 (모든 노드 타입의 텍스트 표시에 사용)
|
|
||||||
/// </summary>
|
|
||||||
public bool TextFontBold { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 노드 이름 말풍선 배경색 (하단에 표시되는 노드 이름의 배경색)
|
|
||||||
/// </summary>
|
|
||||||
public Color NameBubbleBackColor { get; set; } = Color.Gold;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 노드 이름 말풍선 글자색 (하단에 표시되는 노드 이름의 글자색)
|
|
||||||
/// </summary>
|
|
||||||
public Color NameBubbleForeColor { get; set; } = Color.Black;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 배경 표시 여부 (NodeType.Label인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public bool ShowBackground { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 라벨 패딩 (NodeType.Label인 경우 사용, 픽셀 단위)
|
|
||||||
/// </summary>
|
|
||||||
public int Padding { get; set; } = 8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 파일 경로 (편집용, 저장시엔 사용되지 않음)
|
|
||||||
/// </summary>
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
|
||||||
public string ImagePath { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Base64 인코딩된 이미지 데이터 (JSON 저장용)
|
|
||||||
/// </summary>
|
|
||||||
public string ImageBase64 { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 크기 배율 (NodeType.Image인 경우 사용)
|
|
||||||
/// </summary>
|
|
||||||
public SizeF Scale { get; set; } = new SizeF(1.0f, 1.0f);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 투명도 (NodeType.Image인 경우 사용, 0.0~1.0)
|
|
||||||
/// </summary>
|
|
||||||
public float Opacity { get; set; } = 1.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 회전 각도 (NodeType.Image인 경우 사용, 도 단위)
|
|
||||||
/// </summary>
|
|
||||||
public float Rotation { get; set; } = 0.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 로딩된 이미지 (런타임에서만 사용, JSON 직렬화 제외)
|
|
||||||
/// </summary>
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
|
||||||
public Image LoadedImage { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 기본 생성자
|
|
||||||
/// </summary>
|
|
||||||
public MapNode()
|
|
||||||
{
|
{
|
||||||
|
Type = NodeType.Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public MapNode(string nodeId, Point position, StationType type) : base(nodeId, position)
|
||||||
/// 매개변수 생성자
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeId">노드 ID</param>
|
|
||||||
/// <param name="name">노드 이름</param>
|
|
||||||
/// <param name="position">위치</param>
|
|
||||||
/// <param name="type">노드 타입</param>
|
|
||||||
public MapNode(string nodeId, string name, Point position, NodeType type)
|
|
||||||
{
|
{
|
||||||
NodeId = nodeId;
|
Type = NodeType.Normal;
|
||||||
Name = name;
|
|
||||||
Position = position;
|
|
||||||
Type = type;
|
|
||||||
CreatedDate = DateTime.Now;
|
|
||||||
ModifiedDate = DateTime.Now;
|
|
||||||
|
|
||||||
// 타입별 기본 색상 설정
|
|
||||||
SetDefaultColorByType(type);
|
|
||||||
}
|
}
|
||||||
|
[Category("기본 정보")]
|
||||||
/// <summary>
|
[JsonIgnore]
|
||||||
/// 노드 타입에 따른 기본 색상 설정
|
[ReadOnly(true), Browsable(false)]
|
||||||
/// </summary>
|
public bool isDockingNode
|
||||||
/// <param name="type">노드 타입</param>
|
|
||||||
public void SetDefaultColorByType(NodeType type)
|
|
||||||
{
|
{
|
||||||
switch (type)
|
get
|
||||||
{
|
{
|
||||||
case NodeType.Normal:
|
if (StationType == StationType.Charger || StationType == StationType.Buffer ||
|
||||||
DisplayColor = Color.Blue;
|
StationType == StationType.Clearner || StationType == StationType.Loader ||
|
||||||
break;
|
StationType == StationType.UnLoader) return true;
|
||||||
case NodeType.Rotation:
|
return false;
|
||||||
DisplayColor = Color.Orange;
|
|
||||||
break;
|
|
||||||
case NodeType.Docking:
|
|
||||||
DisplayColor = Color.Green;
|
|
||||||
break;
|
|
||||||
case NodeType.Charging:
|
|
||||||
DisplayColor = Color.Red;
|
|
||||||
break;
|
|
||||||
case NodeType.Label:
|
|
||||||
DisplayColor = Color.Purple;
|
|
||||||
break;
|
|
||||||
case NodeType.Image:
|
|
||||||
DisplayColor = Color.Brown;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 다른 노드와의 연결 추가
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeId">연결할 노드 ID</param>
|
|
||||||
public void AddConnection(string nodeId)
|
public void AddConnection(string nodeId)
|
||||||
{
|
{
|
||||||
if (!ConnectedNodes.Contains(nodeId))
|
if (!ConnectedNodes.Contains(nodeId))
|
||||||
@@ -271,10 +131,6 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 다른 노드와의 연결 제거
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeId">연결 해제할 노드 ID</param>
|
|
||||||
public void RemoveConnection(string nodeId)
|
public void RemoveConnection(string nodeId)
|
||||||
{
|
{
|
||||||
if (ConnectedNodes.Remove(nodeId))
|
if (ConnectedNodes.Remove(nodeId))
|
||||||
@@ -283,289 +139,29 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 도킹 스테이션 설정
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stationId">장비 ID</param>
|
|
||||||
/// <param name="stationType">장비 타입</param>
|
|
||||||
/// <param name="dockDirection">도킹 방향</param>
|
|
||||||
public void SetDockingStation(string stationId, StationType stationType, DockingDirection dockDirection)
|
|
||||||
{
|
|
||||||
Type = NodeType.Docking;
|
|
||||||
StationId = stationId;
|
|
||||||
StationType = stationType;
|
|
||||||
DockDirection = dockDirection;
|
|
||||||
SetDefaultColorByType(NodeType.Docking);
|
|
||||||
ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 충전 스테이션 설정
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stationId">충전기 ID</param>
|
|
||||||
public void SetChargingStation(string stationId)
|
public void SetChargingStation(string stationId)
|
||||||
{
|
{
|
||||||
Type = NodeType.Charging;
|
StationType = StationType.Charger;
|
||||||
StationId = stationId;
|
Id = stationId;
|
||||||
StationType = Models.StationType.Charger;
|
DockDirection = DockingDirection.Forward;
|
||||||
DockDirection = DockingDirection.Forward; // 충전기는 항상 전진 도킹
|
|
||||||
SetDefaultColorByType(NodeType.Charging);
|
|
||||||
ModifiedDate = DateTime.Now;
|
ModifiedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 문자열 표현
|
|
||||||
/// </summary>
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{RfidId}({NodeId}): {Name} ({Type}) at ({Position.X}, {Position.Y})";
|
return $"RFID:{RfidId}(NODE:{Id}): AS:{AliasName} ({Type}) at ({Position.X}, {Position.Y})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 리스트박스 표시용 텍스트 (노드ID - 설명 - RFID 순서)
|
|
||||||
/// </summary>
|
|
||||||
public string DisplayText
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var displayText = NodeId;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Name))
|
|
||||||
{
|
|
||||||
displayText += $" - {Name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(RfidId))
|
|
||||||
{
|
|
||||||
displayText += $" - [{RfidId}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 노드 복사
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>복사된 노드</returns>
|
|
||||||
public MapNode Clone()
|
|
||||||
{
|
|
||||||
var clone = new MapNode
|
|
||||||
{
|
|
||||||
NodeId = NodeId,
|
|
||||||
Name = Name,
|
|
||||||
Position = Position,
|
|
||||||
Type = Type,
|
|
||||||
DockDirection = DockDirection,
|
|
||||||
ConnectedNodes = new List<string>(ConnectedNodes),
|
|
||||||
CanRotate = CanRotate,
|
|
||||||
StationId = StationId,
|
|
||||||
StationType = StationType,
|
|
||||||
CreatedDate = CreatedDate,
|
|
||||||
ModifiedDate = ModifiedDate,
|
|
||||||
IsActive = IsActive,
|
|
||||||
DisplayColor = DisplayColor,
|
|
||||||
RfidId = RfidId,
|
|
||||||
RfidStatus = RfidStatus,
|
|
||||||
RfidDescription = RfidDescription,
|
|
||||||
LabelText = LabelText,
|
|
||||||
FontFamily = FontFamily,
|
|
||||||
FontSize = FontSize,
|
|
||||||
FontStyle = FontStyle,
|
|
||||||
ForeColor = ForeColor,
|
|
||||||
BackColor = BackColor,
|
|
||||||
TextFontSize = TextFontSize,
|
|
||||||
TextFontBold = TextFontBold,
|
|
||||||
NameBubbleBackColor = NameBubbleBackColor,
|
|
||||||
NameBubbleForeColor = NameBubbleForeColor,
|
|
||||||
ShowBackground = ShowBackground,
|
|
||||||
Padding = Padding,
|
|
||||||
ImagePath = ImagePath,
|
|
||||||
ImageBase64 = ImageBase64,
|
|
||||||
Scale = Scale,
|
|
||||||
Opacity = Opacity,
|
|
||||||
Rotation = Rotation
|
|
||||||
};
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 로드 (Base64 또는 파일 경로에서, 256x256 이상일 경우 자동 리사이즈)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>로드 성공 여부</returns>
|
|
||||||
public bool LoadImage()
|
|
||||||
{
|
|
||||||
if (Type != NodeType.Image) return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Image originalImage = null;
|
|
||||||
|
|
||||||
// 1. 먼저 Base64 데이터 시도
|
|
||||||
if (!string.IsNullOrEmpty(ImageBase64))
|
|
||||||
{
|
|
||||||
originalImage = ImageConverterUtil.Base64ToImage(ImageBase64);
|
|
||||||
}
|
|
||||||
// 2. Base64가 없으면 파일 경로에서 로드
|
|
||||||
else if (!string.IsNullOrEmpty(ImagePath) && System.IO.File.Exists(ImagePath))
|
|
||||||
{
|
|
||||||
originalImage = Image.FromFile(ImagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalImage != null)
|
|
||||||
{
|
|
||||||
LoadedImage?.Dispose();
|
|
||||||
|
|
||||||
// 이미지 크기 체크 및 리사이즈
|
|
||||||
if (originalImage.Width > 256 || originalImage.Height > 256)
|
|
||||||
{
|
|
||||||
LoadedImage = ResizeImage(originalImage, 256, 256);
|
|
||||||
originalImage.Dispose();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LoadedImage = originalImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// 이미지 로드 실패
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이미지 리사이즈 (비율 유지)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="image">원본 이미지</param>
|
|
||||||
/// <param name="maxWidth">최대 너비</param>
|
|
||||||
/// <param name="maxHeight">최대 높이</param>
|
|
||||||
/// <returns>리사이즈된 이미지</returns>
|
|
||||||
private Image ResizeImage(Image image, int maxWidth, int maxHeight)
|
|
||||||
{
|
|
||||||
// 비율 계산
|
|
||||||
double ratioX = (double)maxWidth / image.Width;
|
|
||||||
double ratioY = (double)maxHeight / image.Height;
|
|
||||||
double ratio = Math.Min(ratioX, ratioY);
|
|
||||||
|
|
||||||
// 새로운 크기 계산
|
|
||||||
int newWidth = (int)(image.Width * ratio);
|
|
||||||
int newHeight = (int)(image.Height * ratio);
|
|
||||||
|
|
||||||
// 리사이즈된 이미지 생성
|
|
||||||
var resizedImage = new Bitmap(newWidth, newHeight);
|
|
||||||
using (var graphics = Graphics.FromImage(resizedImage))
|
|
||||||
{
|
|
||||||
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
|
||||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
||||||
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
|
||||||
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resizedImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 실제 표시될 크기 계산 (이미지 노드인 경우)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>실제 크기</returns>
|
|
||||||
public Size GetDisplaySize()
|
|
||||||
{
|
|
||||||
if (Type != NodeType.Image || LoadedImage == null) return Size.Empty;
|
|
||||||
|
|
||||||
return new Size(
|
|
||||||
(int)(LoadedImage.Width * Scale.Width),
|
|
||||||
(int)(LoadedImage.Height * Scale.Height)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 파일 경로에서 이미지를 Base64로 변환하여 저장
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filePath">이미지 파일 경로</param>
|
|
||||||
/// <returns>변환 성공 여부</returns>
|
|
||||||
public bool ConvertImageToBase64(string filePath)
|
|
||||||
{
|
|
||||||
if (Type != NodeType.Image) return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!System.IO.File.Exists(filePath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageBase64 = ImageConverterUtil.FileToBase64(filePath, System.Drawing.Imaging.ImageFormat.Png);
|
|
||||||
ImagePath = filePath; // 편집용으로 경로 유지
|
|
||||||
|
|
||||||
return !string.IsNullOrEmpty(ImageBase64);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 리소스 정리
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
LoadedImage?.Dispose();
|
|
||||||
LoadedImage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 경로 찾기에 사용 가능한 노드인지 확인
|
|
||||||
/// (라벨, 이미지 노드는 경로 찾기에서 제외)
|
|
||||||
/// </summary>
|
|
||||||
public bool IsNavigationNode()
|
public bool IsNavigationNode()
|
||||||
{
|
{
|
||||||
return Type != NodeType.Label && Type != NodeType.Image && IsActive;
|
// 이제 MapNode는 항상 내비게이션 노드임 (Label, Image 분리됨)
|
||||||
|
// 하지만 기존 로직 호환성을 위해 Active 체크만 유지
|
||||||
|
return IsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID가 할당되어 있는지 확인
|
|
||||||
/// </summary>
|
|
||||||
public bool HasRfid()
|
public bool HasRfid()
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(RfidId);
|
return RfidId > 0;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID 정보 설정
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rfidId">RFID ID</param>
|
|
||||||
/// <param name="rfidDescription">설치 위치 설명</param>
|
|
||||||
/// <param name="rfidStatus">RFID 상태</param>
|
|
||||||
public void SetRfidInfo(string rfidId, string rfidDescription = "", string rfidStatus = "정상")
|
|
||||||
{
|
|
||||||
RfidId = rfidId;
|
|
||||||
RfidDescription = rfidDescription;
|
|
||||||
RfidStatus = rfidStatus;
|
|
||||||
ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID 정보 삭제
|
|
||||||
/// </summary>
|
|
||||||
public void ClearRfidInfo()
|
|
||||||
{
|
|
||||||
RfidId = string.Empty;
|
|
||||||
RfidDescription = string.Empty;
|
|
||||||
RfidStatus = "정상";
|
|
||||||
ModifiedDate = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RFID 기반 표시 텍스트 (RFID ID 우선, 없으면 노드ID)
|
|
||||||
/// </summary>
|
|
||||||
public string GetRfidDisplayText()
|
|
||||||
{
|
|
||||||
return HasRfid() ? RfidId : NodeId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
61
Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs
Normal file
61
Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 맵 상의 모든 객체의 최상위 기본 클래스
|
||||||
|
/// 위치, 선택 상태, 기본 식별자 등을 관리
|
||||||
|
/// </summary>
|
||||||
|
public abstract class NodeBase
|
||||||
|
{
|
||||||
|
[Category("기본 정보")]
|
||||||
|
[Description("객체의 고유 ID입니다.")]
|
||||||
|
[ReadOnly(true)]
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
[Category("기본 정보")]
|
||||||
|
public NodeType Type { protected set; get; } = NodeType.Normal;
|
||||||
|
|
||||||
|
[Category("기본 정보")]
|
||||||
|
[Description("객체의 좌표(X, Y)입니다.")]
|
||||||
|
public virtual Point Position { get; set; } = Point.Empty;
|
||||||
|
|
||||||
|
[Category("기본 정보")]
|
||||||
|
[Description("객체 생성 일자입니다.")]
|
||||||
|
[JsonIgnore]
|
||||||
|
[ReadOnly(true), Browsable(false)]
|
||||||
|
public DateTime CreatedDate { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[Category("기본 정보")]
|
||||||
|
[Description("객체 수정 일자입니다.")]
|
||||||
|
[JsonIgnore]
|
||||||
|
[ReadOnly(true), Browsable(false)]
|
||||||
|
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool IsSelected { get; set; } = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool IsHovered { get; set; } = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public NodeBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeBase(string id, Point position)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Position = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,8 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<string> ErrorOccurred;
|
public event EventHandler<string> ErrorOccurred;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Fields
|
#region Fields
|
||||||
@@ -61,12 +63,12 @@ namespace AGVNavigationCore.Models
|
|||||||
private int _currentNodeIndex;
|
private int _currentNodeIndex;
|
||||||
private MapNode _currentNode;
|
private MapNode _currentNode;
|
||||||
private MapNode _prevNode;
|
private MapNode _prevNode;
|
||||||
|
private AGVTurn _turn;
|
||||||
|
|
||||||
// 이동 관련
|
// 이동 관련
|
||||||
private DateTime _lastUpdateTime;
|
private DateTime _lastUpdateTime;
|
||||||
private Point _moveStartPosition;
|
private Point _moveStartPosition;
|
||||||
private Point _moveTargetPosition;
|
private Point _moveTargetPosition;
|
||||||
private float _moveProgress;
|
|
||||||
|
|
||||||
// 도킹 관련
|
// 도킹 관련
|
||||||
private DockingDirection _dockingDirection;
|
private DockingDirection _dockingDirection;
|
||||||
@@ -79,17 +81,25 @@ namespace AGVNavigationCore.Models
|
|||||||
private List<string> _detectedRfids = new List<string>(); // 감지된 RFID 목록
|
private List<string> _detectedRfids = new List<string>(); // 감지된 RFID 목록
|
||||||
private bool _isPositionConfirmed = false; // 위치 확정 여부 (RFID 2개 이상 감지)
|
private bool _isPositionConfirmed = false; // 위치 확정 여부 (RFID 2개 이상 감지)
|
||||||
|
|
||||||
|
// 에뮬레이터용 추가 속성
|
||||||
|
public double Angle { get; set; } = 0; // 0 = Right, 90 = Down, 180 = Left, 270 = Up (Standard Math)
|
||||||
|
// But AGV Direction: Forward usually means "Front of AGV".
|
||||||
|
// Let's assume Angle is the orientation of the AGV in degrees.
|
||||||
|
|
||||||
|
public bool IsStopMarkOn { get; set; } = false;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
|
public bool Turn180 { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 대상 이동시 모터 방향
|
/// 대상 이동시 모터 방향
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AgvDirection PrevDirection => _prevDirection;
|
public AgvDirection PrevDirection => _prevDirection;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV ID
|
/// AGV ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -136,7 +146,12 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 현재 노드 ID
|
/// 현재 노드 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CurrentNodeId => _currentNode?.NodeId;
|
public MapNode CurrentNode => _currentNode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 노드 ID (CurrentNode.Id)
|
||||||
|
/// </summary>
|
||||||
|
public string CurrentNodeId => _currentNode?.Id;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 이전 위치
|
/// 이전 위치
|
||||||
@@ -148,16 +163,17 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float BatteryLevel { get; set; } = 100.0f;
|
public float BatteryLevel { get; set; } = 100.0f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이전 노드 ID
|
|
||||||
/// </summary>
|
|
||||||
public string PrevNodeId => _prevNode?.NodeId;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 이전 노드
|
/// 이전 노드
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MapNode PrevNode => _prevNode;
|
public MapNode PrevNode => _prevNode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Turn 상태값
|
||||||
|
/// </summary>
|
||||||
|
public AGVTurn Turn { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 도킹 방향
|
/// 도킹 방향
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -173,6 +189,11 @@ namespace AGVNavigationCore.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int DetectedRfidCount => _detectedRfids.Count;
|
public int DetectedRfidCount => _detectedRfids.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 배터리 부족 경고 임계값 (%)
|
||||||
|
/// </summary>
|
||||||
|
public float LowBatteryThreshold { get; set; } = 20.0f;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -209,6 +230,8 @@ namespace AGVNavigationCore.Models
|
|||||||
_currentPosition = position;
|
_currentPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 감지된 RFID 설정 (실제 RFID 센서에서)
|
/// 감지된 RFID 설정 (실제 RFID 센서에서)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -245,12 +268,26 @@ namespace AGVNavigationCore.Models
|
|||||||
BatteryLevel = Math.Max(0, Math.Min(100, percentage));
|
BatteryLevel = Math.Max(0, Math.Min(100, percentage));
|
||||||
|
|
||||||
// 배터리 부족 경고
|
// 배터리 부족 경고
|
||||||
if (BatteryLevel < 20.0f && _currentState != AGVState.Charging)
|
if (BatteryLevel < LowBatteryThreshold && _currentState != AGVState.Charging)
|
||||||
{
|
{
|
||||||
OnError($"배터리 부족: {BatteryLevel:F1}%");
|
OnError($"배터리 부족: {BatteryLevel:F1}% (기준: {LowBatteryThreshold}%)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 노드id의 개체를 IsPass 로 설정합니다
|
||||||
|
/// </summary>
|
||||||
|
public bool SetCurrentNodeMarkStop()
|
||||||
|
{
|
||||||
|
if (_currentNode == null) return false;
|
||||||
|
if (_currentPath == null) return false;
|
||||||
|
var 미완료된처음노드 = _currentPath.DetailedPath.Where(t => t.IsPass == false).OrderBy(t => t.seq).FirstOrDefault();
|
||||||
|
if (미완료된처음노드 == null) return false;
|
||||||
|
미완료된처음노드.IsPass = true;
|
||||||
|
Console.WriteLine($"미완료된처음노드를 true러치합니다");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 다음 동작 예측 (실제 AGV 제어용)
|
/// 다음 동작 예측 (실제 AGV 제어용)
|
||||||
/// AGV가 지속적으로 호출하여 현재 상태와 예측 상태를 일치시킴
|
/// AGV가 지속적으로 호출하여 현재 상태와 예측 상태를 일치시킴
|
||||||
@@ -266,6 +303,7 @@ namespace AGVNavigationCore.Models
|
|||||||
MotorCommand.Forward,
|
MotorCommand.Forward,
|
||||||
MagnetPosition.S, // 직진
|
MagnetPosition.S, // 직진
|
||||||
SpeedLevel.L, // 저속
|
SpeedLevel.L, // 저속
|
||||||
|
eAGVCommandReason.UnknownPosition,
|
||||||
$"위치 미확정 (RFID {_detectedRfids.Count}/2) - 전진하여 RFID 탐색"
|
$"위치 미확정 (RFID {_detectedRfids.Count}/2) - 전진하여 RFID 탐색"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -277,144 +315,59 @@ namespace AGVNavigationCore.Models
|
|||||||
MotorCommand.Stop,
|
MotorCommand.Stop,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.L,
|
SpeedLevel.L,
|
||||||
$"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.NodeId ?? "알수없음"}"
|
eAGVCommandReason.NoPath,
|
||||||
|
$"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.Id ?? "알수없음"}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 위치 확정됨 + 경로 있음 + 남은 노드 없음 → 정지 (목적지 도착)
|
// 3. 위치 확정됨 + 경로 있음 + 남은 노드 없음 → 정지 (목적지 도착)
|
||||||
var lastNode = _currentPath.DetailedPath.Last();
|
var lastNode = _currentPath.DetailedPath.Last();
|
||||||
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
|
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
|
||||||
|
{
|
||||||
|
// 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지)
|
||||||
|
if (_currentNode != null && _currentNode.Id == lastNode.NodeId)
|
||||||
|
{
|
||||||
|
if (lastNode.IsPass) //이미완료되었다.
|
||||||
{
|
{
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
MotorCommand.Stop,
|
MotorCommand.Stop,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.L,
|
SpeedLevel.L,
|
||||||
$"목적지 도착 - 최종:{_currentNode?.NodeId ?? "알수없음"}"
|
eAGVCommandReason.Complete,
|
||||||
|
$"목적지 도착 - 최종:{_currentNode?.Id ?? "알수없음"}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//마지막노드는 일혔지만 완료되지 않았다. 마크스탑필요
|
||||||
|
return new AGVCommand(
|
||||||
|
MotorCommand.Stop,
|
||||||
|
MagnetPosition.S,
|
||||||
|
SpeedLevel.L,
|
||||||
|
eAGVCommandReason.MarkStop,
|
||||||
|
$"목적지 도착 전(MarkStop) - 최종:{_currentNode?.Id ?? "알수없음"}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. 경로이탈
|
// 4. 경로이탈
|
||||||
var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.NodeId)).FirstOrDefault();
|
var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.Id)).FirstOrDefault();
|
||||||
if (TargetNode == null)
|
if (TargetNode == null)
|
||||||
{
|
{
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
MotorCommand.Stop,
|
MotorCommand.Stop,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.L,
|
SpeedLevel.L,
|
||||||
$"(재탐색요청)경로이탈 현재위치:{_currentNode.NodeId}"
|
eAGVCommandReason.PathOut,
|
||||||
|
$"(재탐색요청)경로이탈 현재위치:{_currentNode.Id}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 방향체크
|
return GetCommandFromPath(CurrentNode, "경로 실행 시작");
|
||||||
//if(CurrentDirection != TargetNode.MotorDirection)
|
|
||||||
//{
|
|
||||||
// return new AGVCommand(
|
|
||||||
// MotorCommand.Stop,
|
|
||||||
// MagnetPosition.S,
|
|
||||||
// SpeedLevel.L,
|
|
||||||
// $"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
|
|
||||||
// );
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//this.CurrentNodeId
|
|
||||||
|
|
||||||
return GetCommandFromPath(CurrentNodeId, "경로 실행 시작");
|
|
||||||
|
|
||||||
// 4. 위치 확정 + 경로 실행 중 → 현재 상태에 따른 명령 예측
|
|
||||||
switch (_currentState)
|
|
||||||
{
|
|
||||||
case AGVState.Idle:
|
|
||||||
// 🔥 경로가 있다면 이동 시작 (경로 실행 대기 중)
|
|
||||||
if (_currentPath != null && _remainingNodes != null && _remainingNodes.Count > 0)
|
|
||||||
{
|
|
||||||
// DetailedPath에서 다음 노드 정보 가져오기
|
|
||||||
return GetCommandFromPath(_remainingNodes[0], "경로 실행 시작");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 경로가 없으면 대기
|
|
||||||
return new AGVCommand(
|
|
||||||
MotorCommand.Stop,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.L,
|
|
||||||
"대기 중 (경로 없음)"
|
|
||||||
);
|
|
||||||
|
|
||||||
case AGVState.Moving:
|
|
||||||
{
|
|
||||||
// 이동 중 - DetailedPath에서 현재/다음 노드 정보 가져오기
|
|
||||||
if (_currentPath != null && _remainingNodes != null && _remainingNodes.Count > 0)
|
|
||||||
{
|
|
||||||
return GetCommandFromPath(_remainingNodes[0], "이동 중");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 경로 정보가 없으면 기본 명령 (fallback)
|
|
||||||
var motorCmd = _currentDirection == AgvDirection.Forward
|
|
||||||
? MotorCommand.Forward
|
|
||||||
: MotorCommand.Backward;
|
|
||||||
|
|
||||||
return new AGVCommand(
|
|
||||||
motorCmd,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.M,
|
|
||||||
$"이동 중 (DetailedPath 없음)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
case AGVState.Rotating:
|
|
||||||
// 회전 중 - 정지 상태에서 마그넷만 조정
|
|
||||||
MagnetPosition magnetPos = MagnetPosition.S;
|
|
||||||
if (_currentDirection == AgvDirection.Left)
|
|
||||||
magnetPos = MagnetPosition.L;
|
|
||||||
else if (_currentDirection == AgvDirection.Right)
|
|
||||||
magnetPos = MagnetPosition.R;
|
|
||||||
|
|
||||||
return new AGVCommand(
|
|
||||||
MotorCommand.Stop, // 회전은 정지 상태에서
|
|
||||||
magnetPos,
|
|
||||||
SpeedLevel.L,
|
|
||||||
$"회전 중 ({_currentDirection})"
|
|
||||||
);
|
|
||||||
|
|
||||||
case AGVState.Docking:
|
|
||||||
{
|
|
||||||
// 도킹 중 - 저속으로 전진 또는 후진
|
|
||||||
var dockingMotor = _dockingDirection == DockingDirection.Forward
|
|
||||||
? MotorCommand.Forward
|
|
||||||
: MotorCommand.Backward;
|
|
||||||
|
|
||||||
return new AGVCommand(
|
|
||||||
dockingMotor,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.L, // 도킹은 항상 저속
|
|
||||||
$"도킹 중 ({_dockingDirection})"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
case AGVState.Charging:
|
|
||||||
return new AGVCommand(
|
|
||||||
MotorCommand.Stop,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.L,
|
|
||||||
"충전 중"
|
|
||||||
);
|
|
||||||
|
|
||||||
case AGVState.Error:
|
|
||||||
return new AGVCommand(
|
|
||||||
MotorCommand.Stop,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.L,
|
|
||||||
"오류 발생"
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return new AGVCommand(
|
|
||||||
MotorCommand.Stop,
|
|
||||||
MagnetPosition.S,
|
|
||||||
SpeedLevel.L,
|
|
||||||
"알 수 없는 상태"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -434,7 +387,7 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 현재 노드 ID 조회
|
/// 현재 노드 ID 조회
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetCurrentNodeId() => _currentNode?.NodeId;
|
public MapNode GetCurrentNode() => _currentNode;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV 정보 조회
|
/// AGV 정보 조회
|
||||||
@@ -463,13 +416,13 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
_currentPath = path;
|
_currentPath = path;
|
||||||
_remainingNodes = path.Path.Select(n => n.NodeId).ToList(); // MapNode → NodeId 변환
|
_remainingNodes = path.Path.Select(n => n.Id).ToList(); // MapNode → NodeId 변환
|
||||||
_currentNodeIndex = 0;
|
_currentNodeIndex = 0;
|
||||||
|
|
||||||
// 경로 시작 노드가 현재 노드와 다른 경우 경고
|
// 경로 시작 노드가 현재 노드와 다른 경우 경고
|
||||||
if (_currentNode != null && _remainingNodes.Count > 0 && _remainingNodes[0] != _currentNode.NodeId)
|
if (_currentNode != null && _remainingNodes.Count > 0 && _remainingNodes[0] != _currentNode.Id)
|
||||||
{
|
{
|
||||||
OnError($"경로 시작 노드({_remainingNodes[0]})와 현재 노드({_currentNode.NodeId})가 다릅니다.");
|
OnError($"경로 시작 노드({_remainingNodes[0]})와 현재 노드({_currentNode.Id})가 다릅니다.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,6 +447,27 @@ namespace AGVNavigationCore.Models
|
|||||||
OnError("긴급 정지가 실행되었습니다.");
|
OnError("긴급 정지가 실행되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 일시 정지 (경로 유지)
|
||||||
|
/// </summary>
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
_isMoving = false;
|
||||||
|
_currentSpeed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이동 재개
|
||||||
|
/// </summary>
|
||||||
|
public void Resume()
|
||||||
|
{
|
||||||
|
if (_currentPath != null && _remainingNodes != null && _remainingNodes.Count > 0)
|
||||||
|
{
|
||||||
|
_isMoving = true;
|
||||||
|
SetState(AGVState.Moving);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods - 프레임 업데이트 (외부에서 정기적으로 호출)
|
#region Public Methods - 프레임 업데이트 (외부에서 정기적으로 호출)
|
||||||
@@ -527,10 +501,10 @@ namespace AGVNavigationCore.Models
|
|||||||
_prevPosition = targetPosition;
|
_prevPosition = targetPosition;
|
||||||
_moveStartPosition = _currentPosition;
|
_moveStartPosition = _currentPosition;
|
||||||
_moveTargetPosition = targetPosition;
|
_moveTargetPosition = targetPosition;
|
||||||
_moveProgress = 0;
|
|
||||||
|
|
||||||
SetState(AGVState.Moving);
|
SetState(AGVState.Moving);
|
||||||
_isMoving = true;
|
_isMoving = true;
|
||||||
|
Turn = AGVTurn.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -583,7 +557,7 @@ namespace AGVNavigationCore.Models
|
|||||||
public void SetPosition(MapNode node, AgvDirection motorDirection)
|
public void SetPosition(MapNode node, AgvDirection motorDirection)
|
||||||
{
|
{
|
||||||
// 현재 위치를 이전 위치로 저장 (리프트 방향 계산용)
|
// 현재 위치를 이전 위치로 저장 (리프트 방향 계산용)
|
||||||
if (_currentNode != null && _currentNode.NodeId != node.NodeId)
|
if (_currentNode != null && _currentNode.Id != node.Id)
|
||||||
{
|
{
|
||||||
_prevPosition = _currentPosition; // 이전 위치
|
_prevPosition = _currentPosition; // 이전 위치
|
||||||
_prevNode = _currentNode;
|
_prevNode = _currentNode;
|
||||||
@@ -602,9 +576,9 @@ namespace AGVNavigationCore.Models
|
|||||||
_currentNode = node;
|
_currentNode = node;
|
||||||
|
|
||||||
// 🔥 노드 ID를 RFID로 간주하여 감지 목록에 추가 (시뮬레이터용)
|
// 🔥 노드 ID를 RFID로 간주하여 감지 목록에 추가 (시뮬레이터용)
|
||||||
if (!string.IsNullOrEmpty(node.NodeId) && !_detectedRfids.Contains(node.NodeId))
|
if (!string.IsNullOrEmpty(node.Id) && !_detectedRfids.Contains(node.Id))
|
||||||
{
|
{
|
||||||
_detectedRfids.Add(node.NodeId);
|
_detectedRfids.Add(node.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔥 RFID 2개 이상 감지 시 위치 확정
|
// 🔥 RFID 2개 이상 감지 시 위치 확정
|
||||||
@@ -616,11 +590,19 @@ namespace AGVNavigationCore.Models
|
|||||||
//현재 경로값이 있는지 확인한다.
|
//현재 경로값이 있는지 확인한다.
|
||||||
if (CurrentPath != null && CurrentPath.DetailedPath != null && CurrentPath.DetailedPath.Any())
|
if (CurrentPath != null && CurrentPath.DetailedPath != null && CurrentPath.DetailedPath.Any())
|
||||||
{
|
{
|
||||||
var item = CurrentPath.DetailedPath.FirstOrDefault(t => t.NodeId == node.NodeId && t.IsPass == false);
|
var item = CurrentPath.DetailedPath.FirstOrDefault(t => t.NodeId == node.Id && t.IsPass == false);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
//item.IsPass = true;
|
// [PathJump Check] 점프한 노드 개수 확인
|
||||||
|
// 현재 노드(item)보다 이전인데 아직 IsPass가 안 된 노드의 개수
|
||||||
|
int skippedCount = CurrentPath.DetailedPath.Count(t => t.seq < item.seq && t.IsPass == false);
|
||||||
|
if (skippedCount > 2)
|
||||||
|
{
|
||||||
|
OnError($"PathJump: {skippedCount}개의 노드를 건너뛰었습니다. (허용: 2개, 현재노드: {node.Id})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//item.IsPass = true;
|
||||||
//이전노드는 모두 지나친걸로 한다
|
//이전노드는 모두 지나친걸로 한다
|
||||||
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
|
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
|
||||||
}
|
}
|
||||||
@@ -630,45 +612,46 @@ namespace AGVNavigationCore.Models
|
|||||||
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 RFID 시뮬레이션 (현재 위치 기준)
|
|
||||||
/// </summary>
|
|
||||||
public string SimulateRfidReading(List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
var closestNode = FindClosestNode(_currentPosition, mapNodes);
|
|
||||||
if (closestNode == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return closestNode.HasRfid() ? closestNode.RfidId : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
|
||||||
|
/// </summary>
|
||||||
|
public ushort GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId)
|
||||||
|
{
|
||||||
|
var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId);
|
||||||
|
if ((node?.HasRfid() ?? false) == false) return 0;
|
||||||
|
return node.RfidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DetailedPath에서 노드 정보를 찾아 AGVCommand 생성
|
/// DetailedPath에서 노드 정보를 찾아 AGVCommand 생성
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private AGVCommand GetCommandFromPath(string targetNodeId, string actionDescription)
|
private AGVCommand GetCommandFromPath(MapNode targetNode, string actionDescription)
|
||||||
{
|
{
|
||||||
// DetailedPath가 없으면 기본 명령 반환
|
// DetailedPath가 없으면 기본 명령 반환
|
||||||
if (_currentPath == null || _currentPath.DetailedPath == null || _currentPath.DetailedPath.Count == 0)
|
if (_currentPath == null || _currentPath.DetailedPath == null || _currentPath.DetailedPath.Count == 0)
|
||||||
{
|
{
|
||||||
var defaultMotor = _currentDirection == AgvDirection.Forward
|
// [Refactor] Predict와 일관성 유지: 경로가 없으면 정지
|
||||||
? MotorCommand.Forward
|
|
||||||
: MotorCommand.Backward;
|
|
||||||
|
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
defaultMotor,
|
MotorCommand.Stop,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.M,
|
SpeedLevel.L,
|
||||||
|
eAGVCommandReason.NoPath,
|
||||||
$"{actionDescription} (DetailedPath 없음)"
|
$"{actionDescription} (DetailedPath 없음)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetailedPath에서 targetNodeId에 해당하는 NodeMotorInfo 찾기
|
// DetailedPath에서 targetNodeId에 해당하는 NodeMotorInfo 찾기
|
||||||
// 지나가지 않은 경로를 찾는다
|
// 지나가지 않은 경로를 찾는다
|
||||||
var nodeInfo = _currentPath.DetailedPath.FirstOrDefault(n => n.NodeId == targetNodeId && n.IsPass == false);
|
var nodeInfo = _currentPath.DetailedPath.FirstOrDefault(n => n.NodeId == targetNode.Id && n.IsPass == false);
|
||||||
|
|
||||||
if (nodeInfo == null)
|
if (nodeInfo == null)
|
||||||
{
|
{
|
||||||
@@ -681,7 +664,8 @@ namespace AGVNavigationCore.Models
|
|||||||
defaultMotor,
|
defaultMotor,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.M,
|
SpeedLevel.M,
|
||||||
$"{actionDescription} (노드 {targetNodeId} 정보 없음)"
|
eAGVCommandReason.NoTarget,
|
||||||
|
$"{actionDescription} (노드 {targetNode.Id} 정보 없음)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,16 +700,20 @@ namespace AGVNavigationCore.Models
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 속도 결정 (회전 노드면 저속, 일반 이동은 중속)
|
// [Speed Control] NodeMotorInfo에 설정된 속도 사용
|
||||||
SpeedLevel speed = nodeInfo.CanRotate || nodeInfo.IsDirectionChangePoint
|
// 단, 회전 구간 등에서 안전을 위해 강제 감속이 필요한 경우 로직 추가 가능
|
||||||
? SpeedLevel.L
|
// 현재는 사용자 설정 우선
|
||||||
: SpeedLevel.M;
|
SpeedLevel speed = nodeInfo.Speed;
|
||||||
|
|
||||||
|
// Optional: 회전 시 강제 감속 로직 (사용자 요청에 따라 주석 처리 또는 제거 가능)
|
||||||
|
// if (nodeInfo.CanRotate || nodeInfo.IsDirectionChangePoint) speed = SpeedLevel.L;
|
||||||
|
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
motorCmd,
|
motorCmd,
|
||||||
magnetPos,
|
magnetPos,
|
||||||
speed,
|
speed,
|
||||||
$"{actionDescription} → {targetNodeId} (Motor:{motorCmd}, Magnet:{magnetPos})"
|
eAGVCommandReason.Normal,
|
||||||
|
$"{actionDescription} → {targetNode.Id} (Motor:{motorCmd}, Magnet:{magnetPos})"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,6 +784,29 @@ namespace AGVNavigationCore.Models
|
|||||||
BatteryLevel = Math.Max(0, BatteryLevel);
|
BatteryLevel = Math.Max(0, BatteryLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MapNode StartNode { get; set; } = null;
|
||||||
|
|
||||||
|
private MapNode _targetnode = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 목적지를 설정합니다. 목적지가 변경되면 경로계산정보가 삭제 됩니다.
|
||||||
|
/// </summary>
|
||||||
|
public MapNode TargetNode
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _targetnode;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_targetnode != value)
|
||||||
|
{
|
||||||
|
_currentPath = null;
|
||||||
|
_targetnode = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessNextNode()
|
private void ProcessNextNode()
|
||||||
{
|
{
|
||||||
if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
|
if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
|
||||||
@@ -859,18 +870,6 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockingDirection GetDockingDirection(NodeType nodeType)
|
|
||||||
{
|
|
||||||
switch (nodeType)
|
|
||||||
{
|
|
||||||
case NodeType.Charging:
|
|
||||||
return DockingDirection.Forward;
|
|
||||||
case NodeType.Docking:
|
|
||||||
return DockingDirection.Backward;
|
|
||||||
default:
|
|
||||||
return DockingDirection.Forward;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnError(string message)
|
private void OnError(string message)
|
||||||
{
|
{
|
||||||
@@ -880,8 +879,6 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region Cleanup
|
#region Cleanup
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
|
|||||||
if (node.IsNavigationNode())
|
if (node.IsNavigationNode())
|
||||||
{
|
{
|
||||||
var junctionInfo = AnalyzeNode(node);
|
var junctionInfo = AnalyzeNode(node);
|
||||||
_junctions[node.NodeId] = junctionInfo;
|
_junctions[node.Id] = junctionInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private JunctionInfo AnalyzeNode(MapNode node)
|
private JunctionInfo AnalyzeNode(MapNode node)
|
||||||
{
|
{
|
||||||
var junction = new JunctionInfo(node.NodeId);
|
var junction = new JunctionInfo(node.Id);
|
||||||
|
|
||||||
// 양방향 연결을 고려하여 모든 연결된 노드 찾기
|
// 양방향 연결을 고려하여 모든 연결된 노드 찾기
|
||||||
var connectedNodes = GetAllConnectedNodes(node);
|
var connectedNodes = GetAllConnectedNodes(node);
|
||||||
@@ -96,16 +96,16 @@ namespace AGVNavigationCore.PathFinding.Analysis
|
|||||||
{
|
{
|
||||||
if (connectedNode != null)
|
if (connectedNode != null)
|
||||||
{
|
{
|
||||||
connected.Add(connectedNode.NodeId);
|
connected.Add(connectedNode.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 역방향 연결된 노드들 (다른 노드에서 이 노드로 연결)
|
// 역방향 연결된 노드들 (다른 노드에서 이 노드로 연결)
|
||||||
foreach (var otherNode in _mapNodes)
|
foreach (var otherNode in _mapNodes)
|
||||||
{
|
{
|
||||||
if (otherNode.NodeId != node.NodeId && otherNode.ConnectedMapNodes.Any(n => n.NodeId == node.NodeId))
|
if (otherNode.Id != node.Id && otherNode.ConnectedMapNodes.Any(n => n.Id == node.Id))
|
||||||
{
|
{
|
||||||
connected.Add(otherNode.NodeId);
|
connected.Add(otherNode.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
|
|||||||
|
|
||||||
foreach (var connectedId in connectedNodes)
|
foreach (var connectedId in connectedNodes)
|
||||||
{
|
{
|
||||||
var connectedNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectedId);
|
var connectedNode = _mapNodes.FirstOrDefault(n => n.Id == connectedId);
|
||||||
if (connectedNode != null)
|
if (connectedNode != null)
|
||||||
{
|
{
|
||||||
double angle = CalculateAngle(junctionNode.Position, connectedNode.Position);
|
double angle = CalculateAngle(junctionNode.Position, connectedNode.Position);
|
||||||
@@ -226,9 +226,9 @@ namespace AGVNavigationCore.PathFinding.Analysis
|
|||||||
return MagnetDirection.Straight;
|
return MagnetDirection.Straight;
|
||||||
|
|
||||||
// 실제 각도 기반으로 마그넷 방향 계산
|
// 실제 각도 기반으로 마그넷 방향 계산
|
||||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == fromNodeId);
|
var fromNode = _mapNodes.FirstOrDefault(n => n.Id == fromNodeId);
|
||||||
var currentNode = _mapNodes.FirstOrDefault(n => n.NodeId == currentNodeId);
|
var currentNode = _mapNodes.FirstOrDefault(n => n.Id == currentNodeId);
|
||||||
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == toNodeId);
|
var toNode = _mapNodes.FirstOrDefault(n => n.Id == toNodeId);
|
||||||
|
|
||||||
if (fromNode == null || currentNode == null || toNode == null)
|
if (fromNode == null || currentNode == null || toNode == null)
|
||||||
return MagnetDirection.Straight;
|
return MagnetDirection.Straight;
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
{
|
{
|
||||||
return DetailedPath.Select(n => n.NodeId).ToList();
|
return DetailedPath.Select(n => n.NodeId).ToList();
|
||||||
}
|
}
|
||||||
return Path?.Select(n => n.NodeId).ToList() ?? new List<string>();
|
return Path?.Select(n => n.Id).ToList() ?? new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -50,36 +50,36 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
// 모든 네비게이션 노드를 PathNode로 변환하고 양방향 연결 생성
|
// 모든 네비게이션 노드를 PathNode로 변환하고 양방향 연결 생성
|
||||||
foreach (var mapNode in _mapNodes)
|
foreach (var mapNode in _mapNodes)
|
||||||
{
|
{
|
||||||
_mapNodeLookup[mapNode.NodeId] = mapNode; // Add to lookup table
|
_mapNodeLookup[mapNode.Id] = mapNode; // Add to lookup table
|
||||||
|
|
||||||
if (mapNode.IsNavigationNode())
|
if (mapNode.IsNavigationNode())
|
||||||
{
|
{
|
||||||
var pathNode = new PathNode(mapNode.NodeId, mapNode.Position);
|
var pathNode = new PathNode(mapNode.Id, mapNode.Position);
|
||||||
_nodeMap[mapNode.NodeId] = pathNode;
|
_nodeMap[mapNode.Id] = pathNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 단일 연결을 양방향으로 확장
|
// 단일 연결을 양방향으로 확장
|
||||||
foreach (var mapNode in _mapNodes)
|
foreach (var mapNode in _mapNodes)
|
||||||
{
|
{
|
||||||
if (mapNode.IsNavigationNode() && _nodeMap.ContainsKey(mapNode.NodeId))
|
if (mapNode.IsNavigationNode() && _nodeMap.ContainsKey(mapNode.Id))
|
||||||
{
|
{
|
||||||
var pathNode = _nodeMap[mapNode.NodeId];
|
var pathNode = _nodeMap[mapNode.Id];
|
||||||
|
|
||||||
foreach (var connectedNode in mapNode.ConnectedMapNodes)
|
foreach (var connectedNode in mapNode.ConnectedMapNodes)
|
||||||
{
|
{
|
||||||
if (connectedNode != null && _nodeMap.ContainsKey(connectedNode.NodeId))
|
if (connectedNode != null && _nodeMap.ContainsKey(connectedNode.Id))
|
||||||
{
|
{
|
||||||
// 양방향 연결 생성 (단일 연결이 양방향을 의미)
|
// 양방향 연결 생성 (단일 연결이 양방향을 의미)
|
||||||
if (!pathNode.ConnectedNodes.Contains(connectedNode.NodeId))
|
if (!pathNode.ConnectedNodes.Contains(connectedNode.Id))
|
||||||
{
|
{
|
||||||
pathNode.ConnectedNodes.Add(connectedNode.NodeId);
|
pathNode.ConnectedNodes.Add(connectedNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectedPathNode = _nodeMap[connectedNode.NodeId];
|
var connectedPathNode = _nodeMap[connectedNode.Id];
|
||||||
if (!connectedPathNode.ConnectedNodes.Contains(mapNode.NodeId))
|
if (!connectedPathNode.ConnectedNodes.Contains(mapNode.Id))
|
||||||
{
|
{
|
||||||
connectedPathNode.ConnectedNodes.Add(mapNode.NodeId);
|
connectedPathNode.ConnectedNodes.Add(mapNode.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
/// <param name="startNodeId">시작 노드 ID</param>
|
/// <param name="startNodeId">시작 노드 ID</param>
|
||||||
/// <param name="endNodeId">목적지 노드 ID</param>
|
/// <param name="endNodeId">목적지 노드 ID</param>
|
||||||
/// <returns>경로 계산 결과</returns>
|
/// <returns>경로 계산 결과</returns>
|
||||||
public AGVPathResult FindPath(string startNodeId, string endNodeId)
|
public AGVPathResult FindPathAStar(string startNodeId, string endNodeId)
|
||||||
{
|
{
|
||||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
|
||||||
@@ -126,7 +126,6 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
|
|
||||||
var startNode = _nodeMap[startNodeId];
|
var startNode = _nodeMap[startNodeId];
|
||||||
var endNode = _nodeMap[endNodeId];
|
var endNode = _nodeMap[endNodeId];
|
||||||
|
|
||||||
var openSet = new List<PathNode>();
|
var openSet = new List<PathNode>();
|
||||||
var closedSet = new HashSet<string>();
|
var closedSet = new HashSet<string>();
|
||||||
var exploredCount = 0;
|
var exploredCount = 0;
|
||||||
@@ -199,7 +198,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
// 경유지가 없으면 기본 FindPath 호출
|
// 경유지가 없으면 기본 FindPath 호출
|
||||||
if (waypointNodeIds == null || waypointNodeIds.Length == 0)
|
if (waypointNodeIds == null || waypointNodeIds.Length == 0)
|
||||||
{
|
{
|
||||||
return FindPath(startNodeId, endNodeId);
|
return FindPathAStar(startNodeId, endNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 경유지 유효성 검증
|
// 경유지 유효성 검증
|
||||||
@@ -220,7 +219,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
// 경유지가 없으면 기본 경로 계산
|
// 경유지가 없으면 기본 경로 계산
|
||||||
if (validWaypoints.Count == 0)
|
if (validWaypoints.Count == 0)
|
||||||
{
|
{
|
||||||
return FindPath(startNodeId, endNodeId);
|
return FindPathAStar(startNodeId, endNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 첫 번째 경유지가 시작노드와 같은지 검사
|
// 첫 번째 경유지가 시작노드와 같은지 검사
|
||||||
@@ -267,7 +266,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
string waypoint = validWaypoints[i];
|
string waypoint = validWaypoints[i];
|
||||||
|
|
||||||
// 현재 위치에서 경유지까지의 경로 계산
|
// 현재 위치에서 경유지까지의 경로 계산
|
||||||
var segmentResult = FindPath(currentStart, waypoint);
|
var segmentResult = FindPathAStar(currentStart, waypoint);
|
||||||
|
|
||||||
if (!segmentResult.Success)
|
if (!segmentResult.Success)
|
||||||
{
|
{
|
||||||
@@ -295,7 +294,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2단계: 마지막 경유지에서 최종 목적지까지의 경로 계산
|
// 2단계: 마지막 경유지에서 최종 목적지까지의 경로 계산
|
||||||
var finalSegmentResult = FindPath(currentStart, endNodeId);
|
var finalSegmentResult = FindPathAStar(currentStart, endNodeId);
|
||||||
|
|
||||||
if (!finalSegmentResult.Success)
|
if (!finalSegmentResult.Success)
|
||||||
{
|
{
|
||||||
@@ -372,8 +371,8 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>());
|
var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>());
|
||||||
|
|
||||||
// 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교
|
// 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교
|
||||||
string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].NodeId;
|
string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].Id;
|
||||||
string firstNodeOfCurrent = currentResult.Path[0].NodeId;
|
string firstNodeOfCurrent = currentResult.Path[0].Id;
|
||||||
|
|
||||||
if (lastNodeOfPrevious == firstNodeOfCurrent)
|
if (lastNodeOfPrevious == firstNodeOfCurrent)
|
||||||
{
|
{
|
||||||
@@ -442,7 +441,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
AGVPathResult bestResult = null;
|
AGVPathResult bestResult = null;
|
||||||
foreach (var targetId in targetNodeIds)
|
foreach (var targetId in targetNodeIds)
|
||||||
{
|
{
|
||||||
var result = FindPath(startNodeId, targetId);
|
var result = FindPathAStar(startNodeId, targetId);
|
||||||
if (result.Success && (bestResult == null || result.TotalDistance < bestResult.TotalDistance))
|
if (result.Success && (bestResult == null || result.TotalDistance < bestResult.TotalDistance))
|
||||||
{
|
{
|
||||||
bestResult = result;
|
bestResult = result;
|
||||||
@@ -509,8 +508,8 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
float totalDistance = 0;
|
float totalDistance = 0;
|
||||||
for (int i = 0; i < path.Count - 1; i++)
|
for (int i = 0; i < path.Count - 1; i++)
|
||||||
{
|
{
|
||||||
var nodeId1 = path[i].NodeId;
|
var nodeId1 = path[i].Id;
|
||||||
var nodeId2 = path[i + 1].NodeId;
|
var nodeId2 = path[i + 1].Id;
|
||||||
|
|
||||||
if (_nodeMap.ContainsKey(nodeId1) && _nodeMap.ContainsKey(nodeId2))
|
if (_nodeMap.ContainsKey(nodeId1) && _nodeMap.ContainsKey(nodeId2))
|
||||||
{
|
{
|
||||||
@@ -578,7 +577,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// 교차로 노드 찾기
|
// 교차로 노드 찾기
|
||||||
var junctionNode = _mapNodes.FirstOrDefault(n => n.NodeId == junctionNodeId);
|
var junctionNode = _mapNodes.FirstOrDefault(n => n.Id == junctionNodeId);
|
||||||
if (junctionNode == null || junctionNode.ConnectedNodes == null || junctionNode.ConnectedNodes.Count == 0)
|
if (junctionNode == null || junctionNode.ConnectedNodes == null || junctionNode.ConnectedNodes.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -603,7 +602,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
if (connectedNodeId == targetNodeId) continue;
|
if (connectedNodeId == targetNodeId) continue;
|
||||||
|
|
||||||
// 조건 3, 4, 5: 존재하고, 활성 상태이고, 네비게이션 가능
|
// 조건 3, 4, 5: 존재하고, 활성 상태이고, 네비게이션 가능
|
||||||
var connectedNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
var connectedNode = _mapNodes.FirstOrDefault(n => n.Id == connectedNodeId);
|
||||||
if (connectedNode != null && connectedNode.IsActive && connectedNode.IsNavigationNode())
|
if (connectedNode != null && connectedNode.IsActive && connectedNode.IsNavigationNode())
|
||||||
{
|
{
|
||||||
alternateNodes.Add(connectedNode);
|
alternateNodes.Add(connectedNode);
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
private readonly JunctionAnalyzer _junctionAnalyzer;
|
private readonly JunctionAnalyzer _junctionAnalyzer;
|
||||||
private readonly DirectionChangePlanner _directionChangePlanner;
|
private readonly DirectionChangePlanner _directionChangePlanner;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public AGVPathfinder(List<MapNode> mapNodes)
|
public AGVPathfinder(List<MapNode> mapNodes)
|
||||||
{
|
{
|
||||||
_mapNodes = mapNodes ?? new List<MapNode>();
|
_mapNodes = mapNodes ?? new List<MapNode>();
|
||||||
@@ -49,9 +47,10 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
n.IsActive &&
|
n.IsActive &&
|
||||||
n.IsNavigationNode() &&
|
n.IsNavigationNode() &&
|
||||||
n.ConnectedNodes != null &&
|
n.ConnectedNodes != null &&
|
||||||
|
n.DisableCross == false &&
|
||||||
n.ConnectedNodes.Count >= 3 &&
|
n.ConnectedNodes.Count >= 3 &&
|
||||||
n.ConnectedMapNodes.Where(t => t.Type == NodeType.Docking || t.Type == NodeType.Charging).Any() == false &&
|
n.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false &&
|
||||||
n.NodeId != startNode.NodeId
|
n.Id != startNode.Id
|
||||||
).ToList();
|
).ToList();
|
||||||
|
|
||||||
// docking 포인트가 연결된 노드는 제거한다.
|
// docking 포인트가 연결된 노드는 제거한다.
|
||||||
@@ -99,11 +98,12 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
if (pathNode != null &&
|
if (pathNode != null &&
|
||||||
pathNode.IsActive &&
|
pathNode.IsActive &&
|
||||||
pathNode.IsNavigationNode() &&
|
pathNode.IsNavigationNode() &&
|
||||||
|
pathNode.DisableCross == false &&
|
||||||
pathNode.ConnectedNodes != null &&
|
pathNode.ConnectedNodes != null &&
|
||||||
pathNode.ConnectedNodes.Count >= 3 &&
|
pathNode.ConnectedNodes.Count >= 3 &&
|
||||||
pathNode.ConnectedMapNodes.Where(t => t.Type == NodeType.Docking || t.Type == NodeType.Charging).Any() == false)
|
pathNode.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false)
|
||||||
{
|
{
|
||||||
if (pathNode.NodeId.Equals(StartNode.NodeId) == false)
|
if (pathNode.Id.Equals(StartNode.Id) == false)
|
||||||
return pathNode;
|
return pathNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +111,12 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode)
|
||||||
|
{
|
||||||
|
// 기본값으로 경로 탐색 (이전 위치 = 현재 위치, 방향 = 전진)
|
||||||
|
return FindPath(startNode, targetNode, startNode, AgvDirection.Forward, AgvDirection.Forward, false);
|
||||||
|
}
|
||||||
|
|
||||||
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode,
|
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode,
|
||||||
MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false)
|
MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false)
|
||||||
{
|
{
|
||||||
@@ -121,13 +127,17 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
return AGVPathResult.CreateFailure("목적지 노드가 null입니다.", 0, 0);
|
return AGVPathResult.CreateFailure("목적지 노드가 null입니다.", 0, 0);
|
||||||
if (prevNode == null)
|
if (prevNode == null)
|
||||||
return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0);
|
return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0);
|
||||||
if (startNode.NodeId == targetNode.NodeId && targetNode.DockDirection.MatchAGVDirection(prevDirection))
|
if (targetNode.isDockingNode == false && targetNode.Type != NodeType.Normal)
|
||||||
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode,startNode }, new List<AgvDirection>(), 0, 0);
|
return AGVPathResult.CreateFailure("이동 가능한 노드가 아닙니다", 0, 0);
|
||||||
|
|
||||||
|
var tnode = targetNode as MapNode;
|
||||||
|
if (startNode.Id == targetNode.Id && tnode.DockDirection.MatchAGVDirection(prevDirection))
|
||||||
|
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode, startNode }, new List<AgvDirection>(), 0, 0);
|
||||||
|
|
||||||
var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
|
var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
|
||||||
|
|
||||||
//1.목적지까지의 최단거리 경로를 찾는다.
|
//1.목적지까지의 최단거리 경로를 찾는다.
|
||||||
var pathResult = _basicPathfinder.FindPath(startNode.NodeId, targetNode.NodeId);
|
var pathResult = _basicPathfinder.FindPathAStar(startNode.Id, targetNode.Id);
|
||||||
pathResult.PrevNode = prevNode;
|
pathResult.PrevNode = prevNode;
|
||||||
pathResult.PrevDirection = prevDirection;
|
pathResult.PrevDirection = prevDirection;
|
||||||
if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0)
|
if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0)
|
||||||
@@ -146,11 +156,11 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
|
|
||||||
//2.AGV방향과 목적지에 설정된 방향이 일치하면 그대로 진행하면된다.(목적지에 방향이 없는 경우에도 그대로 진행)
|
//2.AGV방향과 목적지에 설정된 방향이 일치하면 그대로 진행하면된다.(목적지에 방향이 없는 경우에도 그대로 진행)
|
||||||
if (targetNode.DockDirection == DockingDirection.DontCare ||
|
if (tnode.DockDirection == DockingDirection.DontCare ||
|
||||||
(targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) ||
|
(tnode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) ||
|
||||||
(targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward))
|
(tnode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward))
|
||||||
{
|
{
|
||||||
if ((nextNodeForward?.NodeId ?? string.Empty) == pathResult.Path[1].NodeId) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다
|
if ((nextNodeForward?.Id ?? string.Empty) == pathResult.Path[1].Id) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다
|
||||||
{
|
{
|
||||||
MakeDetailData(pathResult, currentDirection);
|
MakeDetailData(pathResult, currentDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
@@ -177,10 +187,10 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
//뒤로 이동시 경로상의 처음 만나는 노드가 같다면 그 방향으로 이동하면 된다.
|
//뒤로 이동시 경로상의 처음 만나는 노드가 같다면 그 방향으로 이동하면 된다.
|
||||||
// ⚠️ 단, 현재 방향과 목적지 도킹 방향이 일치해야 함!
|
// ⚠️ 단, 현재 방향과 목적지 도킹 방향이 일치해야 함!
|
||||||
if (nextNodeBackward != null && pathResult.Path.Count > 1 &&
|
if (nextNodeBackward != null && pathResult.Path.Count > 1 &&
|
||||||
nextNodeBackward.NodeId == pathResult.Path[1].NodeId) // ✅ 추가: 현재도 Backward여야 함
|
nextNodeBackward.Id == pathResult.Path[1].Id) // ✅ 추가: 현재도 Backward여야 함
|
||||||
{
|
{
|
||||||
if (targetNode.DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward ||
|
if (tnode.DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward ||
|
||||||
targetNode.DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward)
|
tnode.DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward)
|
||||||
{
|
{
|
||||||
MakeDetailData(pathResult, ReverseDirection);
|
MakeDetailData(pathResult, ReverseDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
@@ -191,12 +201,12 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextNodeForward != null && pathResult.Path.Count > 1 &&
|
if (nextNodeForward != null && pathResult.Path.Count > 1 &&
|
||||||
nextNodeForward.NodeId == pathResult.Path[1].NodeId &&
|
nextNodeForward.Id == pathResult.Path[1].Id &&
|
||||||
targetNode.DockDirection == DockingDirection.Forward &&
|
tnode.DockDirection == DockingDirection.Forward &&
|
||||||
currentDirection == AgvDirection.Forward) // ✅ 추가: 현재도 Forward여야 함
|
currentDirection == AgvDirection.Forward) // ✅ 추가: 현재도 Forward여야 함
|
||||||
{
|
{
|
||||||
if (targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward ||
|
if (tnode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward ||
|
||||||
targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward)
|
tnode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward)
|
||||||
{
|
{
|
||||||
MakeDetailData(pathResult, currentDirection);
|
MakeDetailData(pathResult, currentDirection);
|
||||||
MakeMagnetDirection(pathResult);
|
MakeMagnetDirection(pathResult);
|
||||||
@@ -221,7 +231,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
//진행방향으로 이동했을때 나오는 노드를 사용한다.
|
//진행방향으로 이동했을때 나오는 노드를 사용한다.
|
||||||
if (nextNodeForward != null)
|
if (nextNodeForward != null)
|
||||||
{
|
{
|
||||||
var Path0 = _basicPathfinder.FindPath(startNode.NodeId, nextNodeForward.NodeId);
|
var Path0 = _basicPathfinder.FindPathAStar(startNode.Id, nextNodeForward.Id);
|
||||||
Path0.PrevNode = prevNode;
|
Path0.PrevNode = prevNode;
|
||||||
Path0.PrevDirection = prevDirection;
|
Path0.PrevDirection = prevDirection;
|
||||||
MakeDetailData(Path0, prevDirection);
|
MakeDetailData(Path0, prevDirection);
|
||||||
@@ -259,7 +269,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
JunctionInPath = FindNearestJunction(startNode);
|
JunctionInPath = FindNearestJunction(startNode);
|
||||||
|
|
||||||
//종료노드로부터 가까운 교차로 검색
|
//종료노드로부터 가까운 교차로 검색
|
||||||
if (JunctionInPath == null) JunctionInPath = FindNearestJunction(targetNode);
|
if (JunctionInPath == null) JunctionInPath = FindNearestJunction(tnode);
|
||||||
}
|
}
|
||||||
if (JunctionInPath == null)
|
if (JunctionInPath == null)
|
||||||
return AGVPathResult.CreateFailure("교차로가 없어 경로계산을 할 수 없습니다", 0, 0);
|
return AGVPathResult.CreateFailure("교차로가 없어 경로계산을 할 수 없습니다", 0, 0);
|
||||||
@@ -267,7 +277,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
//경유지를 포함하여 경로를 다시 계산한다.
|
//경유지를 포함하여 경로를 다시 계산한다.
|
||||||
|
|
||||||
//1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다)
|
//1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다)
|
||||||
var path1 = _basicPathfinder.FindPath(startNode.NodeId, JunctionInPath.NodeId);
|
var path1 = _basicPathfinder.FindPathAStar(startNode.Id, JunctionInPath.Id);
|
||||||
path1.PrevNode = prevNode;
|
path1.PrevNode = prevNode;
|
||||||
path1.PrevDirection = prevDirection;
|
path1.PrevDirection = prevDirection;
|
||||||
|
|
||||||
@@ -278,7 +288,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
// ReverseCheck = false; //현재 진행 방향으로 이동해야한다
|
// ReverseCheck = false; //현재 진행 방향으로 이동해야한다
|
||||||
// MakeDetailData(path1, currentDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
|
// MakeDetailData(path1, currentDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
|
||||||
//}
|
//}
|
||||||
if (path1.Path.Count > 1 && nextNodeBackward != null && nextNodeBackward.NodeId.Equals(path1.Path[1].NodeId))
|
if (path1.Path.Count > 1 && nextNodeBackward != null && nextNodeBackward.Id.Equals(path1.Path[1].Id))
|
||||||
{
|
{
|
||||||
ReverseCheck = true; //현재 방향의 반대방향으로 이동해야한다
|
ReverseCheck = true; //현재 방향의 반대방향으로 이동해야한다
|
||||||
MakeDetailData(path1, ReverseDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
|
MakeDetailData(path1, ReverseDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
|
||||||
@@ -295,7 +305,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
|
|
||||||
//2.교차로 - 종료위치
|
//2.교차로 - 종료위치
|
||||||
var path2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, targetNode.NodeId);
|
var path2 = _basicPathfinder.FindPathAStar(JunctionInPath.Id, targetNode.Id);
|
||||||
path2.PrevNode = prevNode;
|
path2.PrevNode = prevNode;
|
||||||
path2.PrevDirection = prevDirection;
|
path2.PrevDirection = prevDirection;
|
||||||
|
|
||||||
@@ -315,9 +325,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
|
|
||||||
//3.방향전환을 위환 대체 노드찾기
|
//3.방향전환을 위환 대체 노드찾기
|
||||||
tempNode = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId,
|
tempNode = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.Id,
|
||||||
path1.Path[path1.Path.Count - 2].NodeId,
|
path1.Path[path1.Path.Count - 2].Id,
|
||||||
path2.Path[1].NodeId);
|
path2.Path[1].Id);
|
||||||
|
|
||||||
//4. path1 + tempnode + path2 가 최종 위치가 된다.
|
//4. path1 + tempnode + path2 가 최종 위치가 된다.
|
||||||
if (tempNode == null)
|
if (tempNode == null)
|
||||||
@@ -332,7 +342,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
//if (tempNode != null)
|
//if (tempNode != null)
|
||||||
{
|
{
|
||||||
// 교차로 → 대체노드 경로 계산
|
// 교차로 → 대체노드 경로 계산
|
||||||
var pathToTemp = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode.NodeId);
|
var pathToTemp = _basicPathfinder.FindPathAStar(JunctionInPath.Id, tempNode.Id);
|
||||||
pathToTemp.PrevNode = JunctionInPath;
|
pathToTemp.PrevNode = JunctionInPath;
|
||||||
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
|
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
|
||||||
if (!pathToTemp.Success)
|
if (!pathToTemp.Success)
|
||||||
@@ -348,7 +358,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
|
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
|
||||||
|
|
||||||
// 대체노드 → 교차로 경로 계산 (역방향)
|
// 대체노드 → 교차로 경로 계산 (역방향)
|
||||||
var pathFromTemp = _basicPathfinder.FindPath(tempNode.NodeId, JunctionInPath.NodeId);
|
var pathFromTemp = _basicPathfinder.FindPathAStar(tempNode.Id, JunctionInPath.Id);
|
||||||
pathFromTemp.PrevNode = JunctionInPath;
|
pathFromTemp.PrevNode = JunctionInPath;
|
||||||
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
|
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
|
||||||
if (!pathFromTemp.Success)
|
if (!pathFromTemp.Success)
|
||||||
@@ -362,14 +372,14 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
//현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다.
|
//현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다.
|
||||||
bool temp3ok = false;
|
bool temp3ok = false;
|
||||||
var TempCheck3 = _basicPathfinder.FindPath(combinedResult.Path.Last().NodeId, targetNode.NodeId);
|
var TempCheck3 = _basicPathfinder.FindPathAStar(combinedResult.Path.Last().Id, targetNode.Id);
|
||||||
if (TempCheck3.Path.First().NodeId.Equals(combinedResult.Path.Last().NodeId))
|
if (TempCheck3.Path.First().Id.Equals(combinedResult.Path.Last().Id))
|
||||||
{
|
{
|
||||||
if (targetNode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward)
|
if (tnode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward)
|
||||||
{
|
{
|
||||||
temp3ok = true;
|
temp3ok = true;
|
||||||
}
|
}
|
||||||
else if (targetNode.DockDirection == DockingDirection.Backward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Backward)
|
else if (tnode.DockDirection == DockingDirection.Backward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Backward)
|
||||||
{
|
{
|
||||||
temp3ok = true;
|
temp3ok = true;
|
||||||
}
|
}
|
||||||
@@ -381,11 +391,11 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
if (temp3ok == false)
|
if (temp3ok == false)
|
||||||
{
|
{
|
||||||
//목적지와 방향이 맞지 않다. 그러므로 대체노드를 추가로 더 찾아야한다.
|
//목적지와 방향이 맞지 않다. 그러므로 대체노드를 추가로 더 찾아야한다.
|
||||||
var tempNode2 = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId,
|
var tempNode2 = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.Id,
|
||||||
combinedResult.Path[combinedResult.Path.Count - 2].NodeId,
|
combinedResult.Path[combinedResult.Path.Count - 2].Id,
|
||||||
path2.Path[1].NodeId);
|
path2.Path[1].Id);
|
||||||
|
|
||||||
var pathToTemp2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode2.NodeId);
|
var pathToTemp2 = _basicPathfinder.FindPathAStar(JunctionInPath.Id, tempNode2.Id);
|
||||||
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
|
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
|
||||||
else MakeDetailData(pathToTemp2, ReverseDirection);
|
else MakeDetailData(pathToTemp2, ReverseDirection);
|
||||||
|
|
||||||
@@ -400,7 +410,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
|
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pathToTemp3 = _basicPathfinder.FindPath(tempNode2.NodeId, JunctionInPath.NodeId);
|
var pathToTemp3 = _basicPathfinder.FindPathAStar(tempNode2.Id, JunctionInPath.Id);
|
||||||
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
|
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
|
||||||
else MakeDetailData(pathToTemp3, currentDirection);
|
else MakeDetailData(pathToTemp3, currentDirection);
|
||||||
|
|
||||||
@@ -420,12 +430,15 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요
|
/// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path1"></param>
|
/// <param name="path1"></param>
|
||||||
/// <param name="currentDirection"></param>
|
/// <param name="currentDirection"></param>
|
||||||
private void MakeDetailData(AGVPathResult path1, AgvDirection currentDirection)
|
public void MakeDetailData(AGVPathResult path1, AgvDirection currentDirection)
|
||||||
{
|
{
|
||||||
if (path1.Success && path1.Path != null && path1.Path.Count > 0)
|
if (path1.Success && path1.Path != null && path1.Path.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -433,18 +446,25 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
for (int i = 0; i < path1.Path.Count; i++)
|
for (int i = 0; i < path1.Path.Count; i++)
|
||||||
{
|
{
|
||||||
var node = path1.Path[i];
|
var node = path1.Path[i];
|
||||||
string nodeId = node.NodeId;
|
string nodeId = node.Id;
|
||||||
string RfidId = node.RfidId;
|
var RfidId = node.RfidId;
|
||||||
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
|
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
|
||||||
|
|
||||||
// 노드 정보 생성 (현재 방향 유지)
|
// 노드 정보 생성 (현재 방향 유지)
|
||||||
var nodeInfo = new NodeMotorInfo(i+1,
|
var nodeInfo = new NodeMotorInfo(i + 1,
|
||||||
nodeId, RfidId,
|
nodeId, RfidId,
|
||||||
currentDirection,
|
currentDirection,
|
||||||
nextNodeId,
|
nextNodeId,
|
||||||
MagnetDirection.Straight
|
MagnetDirection.Straight
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// [Speed Control] MapNode의 속도 설정 적용
|
||||||
|
var mapNode = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
|
if (mapNode != null)
|
||||||
|
{
|
||||||
|
nodeInfo.Speed = mapNode.SpeedLimit;
|
||||||
|
}
|
||||||
|
|
||||||
detailedPath1.Add(nodeInfo);
|
detailedPath1.Add(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,13 +485,13 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
for (int i = 0; i < path1.DetailedPath.Count; i++)
|
for (int i = 0; i < path1.DetailedPath.Count; i++)
|
||||||
{
|
{
|
||||||
var detailPath = path1.DetailedPath[i];
|
var detailPath = path1.DetailedPath[i];
|
||||||
string nodeId = path1.Path[i].NodeId;
|
string nodeId = path1.Path[i].Id;
|
||||||
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
|
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
|
||||||
|
|
||||||
// 마그넷 방향 계산 (3개 이상 연결된 교차로에서만 좌/우 가중치 적용)
|
// 마그넷 방향 계산 (3개 이상 연결된 교차로에서만 좌/우 가중치 적용)
|
||||||
if (i > 0 && nextNodeId != null)
|
if (i > 0 && nextNodeId != null)
|
||||||
{
|
{
|
||||||
string prevNodeId = path1.Path[i - 1].NodeId;
|
string prevNodeId = path1.Path[i - 1].Id;
|
||||||
if (path1.DetailedPath[i - 1].MotorDirection != detailPath.MotorDirection)
|
if (path1.DetailedPath[i - 1].MotorDirection != detailPath.MotorDirection)
|
||||||
detailPath.MagnetDirection = MagnetDirection.Straight;
|
detailPath.MagnetDirection = MagnetDirection.Straight;
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
// 방향이 같으면 직접 경로 계산
|
// 방향이 같으면 직접 경로 계산
|
||||||
if (currentDirection == requiredDirection)
|
if (currentDirection == requiredDirection)
|
||||||
{
|
{
|
||||||
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
|
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
|
||||||
if (directPath.Success)
|
if (directPath.Success)
|
||||||
{
|
{
|
||||||
return DirectionChangePlan.CreateSuccess(
|
return DirectionChangePlan.CreateSuccess(
|
||||||
@@ -85,20 +85,20 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 방향 전환이 필요한 경우 - 먼저 간단한 직접 경로 확인
|
// 방향 전환이 필요한 경우 - 먼저 간단한 직접 경로 확인
|
||||||
var directPath2 = _pathfinder.FindPath(startNodeId, targetNodeId);
|
var directPath2 = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
|
||||||
if (directPath2.Success)
|
if (directPath2.Success)
|
||||||
{
|
{
|
||||||
// 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환
|
// 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환
|
||||||
foreach (var node in directPath2.Path.Skip(1).Take(directPath2.Path.Count - 2)) // 시작과 끝 제외
|
foreach (var node in directPath2.Path.Skip(1).Take(directPath2.Path.Count - 2)) // 시작과 끝 제외
|
||||||
{
|
{
|
||||||
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.NodeId);
|
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.Id);
|
||||||
if (junctionInfo != null && junctionInfo.IsJunction)
|
if (junctionInfo != null && junctionInfo.IsJunction)
|
||||||
{
|
{
|
||||||
// 간단한 방향 전환: 직접 경로 사용하되 방향 전환 노드 표시
|
// 간단한 방향 전환: 직접 경로 사용하되 방향 전환 노드 표시
|
||||||
return DirectionChangePlan.CreateSuccess(
|
return DirectionChangePlan.CreateSuccess(
|
||||||
directPath2.Path,
|
directPath2.Path,
|
||||||
node.NodeId,
|
node.Id,
|
||||||
$"갈림길 {node.NodeId}에서 방향 전환: {currentDirection} → {requiredDirection}"
|
$"갈림길 {node.Id}에서 방향 전환: {currentDirection} → {requiredDirection}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,19 +160,19 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 직진 경로상의 갈림길들도 검색 (단, 되돌아가기 방지)
|
// 2. 직진 경로상의 갈림길들도 검색 (단, 되돌아가기 방지)
|
||||||
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
|
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
|
||||||
if (directPath.Success)
|
if (directPath.Success)
|
||||||
{
|
{
|
||||||
foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외
|
foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외
|
||||||
{
|
{
|
||||||
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.NodeId);
|
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.Id);
|
||||||
if (junctionInfo != null && junctionInfo.IsJunction)
|
if (junctionInfo != null && junctionInfo.IsJunction)
|
||||||
{
|
{
|
||||||
// 직진 경로상에서는 더 엄격한 조건 적용
|
// 직진 경로상에서는 더 엄격한 조건 적용
|
||||||
if (!suitableJunctions.Contains(node.NodeId) &&
|
if (!suitableJunctions.Contains(node.Id) &&
|
||||||
HasMultipleExitOptions(node.NodeId))
|
HasMultipleExitOptions(node.Id))
|
||||||
{
|
{
|
||||||
suitableJunctions.Add(node.NodeId);
|
suitableJunctions.Add(node.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private List<string> GetAllConnectedNodes(string nodeId)
|
private List<string> GetAllConnectedNodes(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
|
var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node == null) return new List<string>();
|
if (node == null) return new List<string>();
|
||||||
|
|
||||||
var connected = new HashSet<string>();
|
var connected = new HashSet<string>();
|
||||||
@@ -236,16 +236,16 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
if (connectedNode != null)
|
if (connectedNode != null)
|
||||||
{
|
{
|
||||||
connected.Add(connectedNode.NodeId);
|
connected.Add(connectedNode.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 역방향 연결
|
// 역방향 연결
|
||||||
foreach (var otherNode in _mapNodes)
|
foreach (var otherNode in _mapNodes)
|
||||||
{
|
{
|
||||||
if (otherNode.NodeId != nodeId && otherNode.ConnectedMapNodes.Any(n => n.NodeId == nodeId))
|
if (otherNode.Id != nodeId && otherNode.ConnectedMapNodes.Any(n => n.Id == nodeId))
|
||||||
{
|
{
|
||||||
connected.Add(otherNode.NodeId);
|
connected.Add(otherNode.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
foreach (var junction in junctions)
|
foreach (var junction in junctions)
|
||||||
{
|
{
|
||||||
var path = _pathfinder.FindPath(startNodeId, junction);
|
var path = _pathfinder.FindPathAStar(startNodeId, junction);
|
||||||
double distance = path.Success ? path.TotalDistance : double.MaxValue;
|
double distance = path.Success ? path.TotalDistance : double.MaxValue;
|
||||||
distances.Add((junction, distance));
|
distances.Add((junction, distance));
|
||||||
}
|
}
|
||||||
@@ -293,7 +293,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
string actualDirectionChangeNode = FindActualDirectionChangeNode(changePath, junctionNodeId);
|
string actualDirectionChangeNode = FindActualDirectionChangeNode(changePath, junctionNodeId);
|
||||||
|
|
||||||
string description = $"갈림길 {GetDisplayName(junctionNodeId)}를 통해 {GetDisplayName(actualDirectionChangeNode)}에서 방향 전환: {currentDirection} → {requiredDirection}";
|
string description = $"갈림길 {GetDisplayName(junctionNodeId)}를 통해 {GetDisplayName(actualDirectionChangeNode)}에서 방향 전환: {currentDirection} → {requiredDirection}";
|
||||||
System.Diagnostics.Debug.WriteLine($"[DirectionChangePlanner] ✅ 유효한 방향전환 경로: {string.Join(" → ", changePath.Select(n => n.NodeId))}");
|
System.Diagnostics.Debug.WriteLine($"[DirectionChangePlanner] ✅ 유효한 방향전환 경로: {string.Join(" → ", changePath.Select(n => n.Id))}");
|
||||||
return DirectionChangePlan.CreateSuccess(changePath, actualDirectionChangeNode, description);
|
return DirectionChangePlan.CreateSuccess(changePath, actualDirectionChangeNode, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,13 +313,13 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
var fullPath = new List<MapNode>();
|
var fullPath = new List<MapNode>();
|
||||||
|
|
||||||
// 1. 시작점에서 갈림길까지의 경로
|
// 1. 시작점에서 갈림길까지의 경로
|
||||||
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
|
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
|
||||||
if (!toJunctionPath.Success)
|
if (!toJunctionPath.Success)
|
||||||
return fullPath;
|
return fullPath;
|
||||||
|
|
||||||
// 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단
|
// 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단
|
||||||
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
|
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
|
||||||
bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.NodeId == junctionNodeId);
|
bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.Id == junctionNodeId);
|
||||||
|
|
||||||
if (isNearbyDetour)
|
if (isNearbyDetour)
|
||||||
{
|
{
|
||||||
@@ -341,7 +341,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
var fullPath = new List<MapNode>();
|
var fullPath = new List<MapNode>();
|
||||||
|
|
||||||
// 1. 시작점에서 갈림길까지 직진 (현재 방향 유지)
|
// 1. 시작점에서 갈림길까지 직진 (현재 방향 유지)
|
||||||
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
|
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
|
||||||
if (!toJunctionPath.Success)
|
if (!toJunctionPath.Success)
|
||||||
return fullPath;
|
return fullPath;
|
||||||
|
|
||||||
@@ -349,7 +349,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
// 2. 갈림길에서 방향 전환 후 목적지로
|
// 2. 갈림길에서 방향 전환 후 목적지로
|
||||||
// 이때 마그넷 센서를 이용해 목적지 방향으로 진입
|
// 이때 마그넷 센서를 이용해 목적지 방향으로 진입
|
||||||
var fromJunctionPath = _pathfinder.FindPath(junctionNodeId, targetNodeId);
|
var fromJunctionPath = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
|
||||||
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
|
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
|
||||||
{
|
{
|
||||||
fullPath.AddRange(fromJunctionPath.Path.Skip(1)); // 중복 노드 제거
|
fullPath.AddRange(fromJunctionPath.Path.Skip(1)); // 중복 노드 제거
|
||||||
@@ -366,7 +366,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
var fullPath = new List<MapNode>();
|
var fullPath = new List<MapNode>();
|
||||||
|
|
||||||
// 1. 시작점에서 갈림길까지의 경로
|
// 1. 시작점에서 갈림길까지의 경로
|
||||||
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
|
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
|
||||||
if (!toJunctionPath.Success)
|
if (!toJunctionPath.Success)
|
||||||
return fullPath;
|
return fullPath;
|
||||||
|
|
||||||
@@ -376,18 +376,18 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
if (currentDirection != requiredDirection)
|
if (currentDirection != requiredDirection)
|
||||||
{
|
{
|
||||||
string fromNodeId = toJunctionPath.Path.Count >= 2 ?
|
string fromNodeId = toJunctionPath.Path.Count >= 2 ?
|
||||||
toJunctionPath.Path[toJunctionPath.Path.Count - 2].NodeId : startNodeId;
|
toJunctionPath.Path[toJunctionPath.Path.Count - 2].Id : startNodeId;
|
||||||
|
|
||||||
var changeSequence = GenerateDirectionChangeSequence(junctionNodeId, fromNodeId, currentDirection, requiredDirection);
|
var changeSequence = GenerateDirectionChangeSequence(junctionNodeId, fromNodeId, currentDirection, requiredDirection);
|
||||||
if (changeSequence.Count > 1)
|
if (changeSequence.Count > 1)
|
||||||
{
|
{
|
||||||
fullPath.AddRange(changeSequence.Skip(1).Select(nodeId => _mapNodes.FirstOrDefault(n => n.NodeId == nodeId)).Where(n => n != null));
|
fullPath.AddRange(changeSequence.Skip(1).Select(nodeId => _mapNodes.FirstOrDefault(n => n.Id == nodeId)).Where(n => n != null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 갈림길에서 목표점까지의 경로
|
// 3. 갈림길에서 목표점까지의 경로
|
||||||
string lastNode = fullPath.LastOrDefault()?.NodeId ?? junctionNodeId;
|
string lastNode = fullPath.LastOrDefault()?.Id ?? junctionNodeId;
|
||||||
var fromJunctionPath = _pathfinder.FindPath(lastNode, targetNodeId);
|
var fromJunctionPath = _pathfinder.FindPathAStar(lastNode, targetNodeId);
|
||||||
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
|
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
|
||||||
{
|
{
|
||||||
fullPath.AddRange(fromJunctionPath.Path.Skip(1));
|
fullPath.AddRange(fromJunctionPath.Path.Skip(1));
|
||||||
@@ -461,8 +461,8 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
// 왔던 길(excludeNodeId)를 제외한 노드 중에서 최적의 우회 노드 선택
|
// 왔던 길(excludeNodeId)를 제외한 노드 중에서 최적의 우회 노드 선택
|
||||||
// 우선순위: 1) 막다른 길이 아닌 노드 (우회 후 복귀 가능) 2) 직진방향 3) 목적지 방향
|
// 우선순위: 1) 막다른 길이 아닌 노드 (우회 후 복귀 가능) 2) 직진방향 3) 목적지 방향
|
||||||
|
|
||||||
var junctionNode = _mapNodes.FirstOrDefault(n => n.NodeId == junctionNodeId);
|
var junctionNode = _mapNodes.FirstOrDefault(n => n.Id == junctionNodeId);
|
||||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == excludeNodeId);
|
var fromNode = _mapNodes.FirstOrDefault(n => n.Id == excludeNodeId);
|
||||||
|
|
||||||
if (junctionNode == null || fromNode == null)
|
if (junctionNode == null || fromNode == null)
|
||||||
return availableNodes.FirstOrDefault();
|
return availableNodes.FirstOrDefault();
|
||||||
@@ -478,7 +478,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
if (nodeId == excludeNodeId) continue; // 왔던 길 제외
|
if (nodeId == excludeNodeId) continue; // 왔던 길 제외
|
||||||
|
|
||||||
var candidateNode = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
|
var candidateNode = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (candidateNode == null) continue;
|
if (candidateNode == null) continue;
|
||||||
|
|
||||||
// 갈림길에서 후보 노드로의 방향 벡터 계산 (junctionNode → candidateNode)
|
// 갈림길에서 후보 노드로의 방향 벡터 계산 (junctionNode → candidateNode)
|
||||||
@@ -561,11 +561,11 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
return junctionNodeId; // 기본값으로 갈림길 반환
|
return junctionNodeId; // 기본값으로 갈림길 반환
|
||||||
|
|
||||||
// 갈림길이 두 번 나타나는 위치 찾기
|
// 갈림길이 두 번 나타나는 위치 찾기
|
||||||
int firstJunctionIndex = changePath.FindIndex(n => n.NodeId == junctionNodeId);
|
int firstJunctionIndex = changePath.FindIndex(n => n.Id == junctionNodeId);
|
||||||
int lastJunctionIndex = -1;
|
int lastJunctionIndex = -1;
|
||||||
for (int i = changePath.Count - 1; i >= 0; i--)
|
for (int i = changePath.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (changePath[i].NodeId == junctionNodeId)
|
if (changePath[i].Id == junctionNodeId)
|
||||||
{
|
{
|
||||||
lastJunctionIndex = i;
|
lastJunctionIndex = i;
|
||||||
break;
|
break;
|
||||||
@@ -577,7 +577,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
firstJunctionIndex != lastJunctionIndex && lastJunctionIndex - firstJunctionIndex == 2)
|
firstJunctionIndex != lastJunctionIndex && lastJunctionIndex - firstJunctionIndex == 2)
|
||||||
{
|
{
|
||||||
// 첫 번째와 두 번째 갈림길 사이에 있는 노드가 실제 방향전환 노드
|
// 첫 번째와 두 번째 갈림길 사이에 있는 노드가 실제 방향전환 노드
|
||||||
string detourNode = changePath[firstJunctionIndex + 1].NodeId;
|
string detourNode = changePath[firstJunctionIndex + 1].Id;
|
||||||
return detourNode;
|
return detourNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +609,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
private bool CanReachTargetViaJunction(string junctionNodeId, string targetNodeId)
|
private bool CanReachTargetViaJunction(string junctionNodeId, string targetNodeId)
|
||||||
{
|
{
|
||||||
// 갈림길에서 목적지까지의 경로가 존재하는지 확인
|
// 갈림길에서 목적지까지의 경로가 존재하는지 확인
|
||||||
var pathToTarget = _pathfinder.FindPath(junctionNodeId, targetNodeId);
|
var pathToTarget = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
|
||||||
return pathToTarget.Success;
|
return pathToTarget.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,11 +647,11 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
}
|
}
|
||||||
|
|
||||||
string errorMessage = $"되돌아가기 패턴 검출 ({backtrackingPatterns.Count}개): {string.Join(", ", issues)}";
|
string errorMessage = $"되돌아가기 패턴 검출 ({backtrackingPatterns.Count}개): {string.Join(", ", issues)}";
|
||||||
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 경로: {string.Join(" → ", path.Select(n => n.NodeId))}");
|
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 경로: {string.Join(" → ", path.Select(n => n.Id))}");
|
||||||
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 되돌아가기 패턴: {errorMessage}");
|
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 되돌아가기 패턴: {errorMessage}");
|
||||||
|
|
||||||
return PathValidationResult.CreateInvalidWithBacktracking(
|
return PathValidationResult.CreateInvalidWithBacktracking(
|
||||||
path.Select(n => n.NodeId).ToList(), backtrackingPatterns, startNodeId, "", junctionNodeId, errorMessage);
|
path.Select(n => n.Id).ToList(), backtrackingPatterns, startNodeId, "", junctionNodeId, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 연속된 중복 노드 검증
|
// 2. 연속된 중복 노드 검증
|
||||||
@@ -670,13 +670,13 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 갈림길 포함 여부 검증
|
// 4. 갈림길 포함 여부 검증
|
||||||
if (!path.Any(n => n.NodeId == junctionNodeId))
|
if (!path.Any(n => n.Id == junctionNodeId))
|
||||||
{
|
{
|
||||||
return PathValidationResult.CreateInvalid(startNodeId, "", $"갈림길 {junctionNodeId}이 경로에 포함되지 않음");
|
return PathValidationResult.CreateInvalid(startNodeId, "", $"갈림길 {junctionNodeId}이 경로에 포함되지 않음");
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[PathValidation] ✅ 유효한 경로: {string.Join(" → ", path.Select(n => n.NodeId))}");
|
System.Diagnostics.Debug.WriteLine($"[PathValidation] ✅ 유효한 경로: {string.Join(" → ", path.Select(n => n.Id))}");
|
||||||
return PathValidationResult.CreateValid(path.Select(n => n.NodeId).ToList(), startNodeId, "", junctionNodeId);
|
return PathValidationResult.CreateValid(path.Select(n => n.Id).ToList(), startNodeId, "", junctionNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -688,9 +688,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
for (int i = 0; i < path.Count - 2; i++)
|
for (int i = 0; i < path.Count - 2; i++)
|
||||||
{
|
{
|
||||||
string nodeA = path[i].NodeId;
|
string nodeA = path[i].Id;
|
||||||
string nodeB = path[i + 1].NodeId;
|
string nodeB = path[i + 1].Id;
|
||||||
string nodeC = path[i + 2].NodeId;
|
string nodeC = path[i + 2].Id;
|
||||||
|
|
||||||
// A → B → A 패턴 검출
|
// A → B → A 패턴 검출
|
||||||
if (nodeA == nodeC && nodeA != nodeB)
|
if (nodeA == nodeC && nodeA != nodeB)
|
||||||
@@ -712,9 +712,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
for (int i = 0; i < path.Count - 1; i++)
|
for (int i = 0; i < path.Count - 1; i++)
|
||||||
{
|
{
|
||||||
if (path[i].NodeId == path[i + 1].NodeId)
|
if (path[i].Id == path[i + 1].Id)
|
||||||
{
|
{
|
||||||
duplicates.Add(path[i].NodeId);
|
duplicates.Add(path[i].Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,12 +728,12 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < path.Count - 1; i++)
|
for (int i = 0; i < path.Count - 1; i++)
|
||||||
{
|
{
|
||||||
string currentNode = path[i].NodeId;
|
string currentNode = path[i].Id;
|
||||||
string nextNode = path[i + 1].NodeId;
|
string nextNode = path[i + 1].Id;
|
||||||
|
|
||||||
// 두 노드간 직접 연결성 확인 (맵 노드의 ConnectedMapNodes 리스트 사용)
|
// 두 노드간 직접 연결성 확인 (맵 노드의 ConnectedMapNodes 리스트 사용)
|
||||||
var currentMapNode = _mapNodes.FirstOrDefault(n => n.NodeId == currentNode);
|
var currentMapNode = _mapNodes.FirstOrDefault(n => n.Id == currentNode);
|
||||||
if (currentMapNode == null || !currentMapNode.ConnectedMapNodes.Any(n => n.NodeId == nextNode))
|
if (currentMapNode == null || !currentMapNode.ConnectedMapNodes.Any(n => n.Id == nextNode))
|
||||||
{
|
{
|
||||||
return PathValidationResult.CreateInvalid(currentNode, nextNode, $"노드 {currentNode}와 {nextNode} 사이에 연결이 없음");
|
return PathValidationResult.CreateInvalid(currentNode, nextNode, $"노드 {currentNode}와 {nextNode} 사이에 연결이 없음");
|
||||||
}
|
}
|
||||||
@@ -769,10 +769,10 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// <returns>표시할 이름</returns>
|
/// <returns>표시할 이름</returns>
|
||||||
private string GetDisplayName(string nodeId)
|
private string GetDisplayName(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
|
var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node != null && !string.IsNullOrEmpty(node.RfidId))
|
if (node != null && node.HasRfid())
|
||||||
{
|
{
|
||||||
return node.RfidId;
|
return node.RfidId.ToString("0000");
|
||||||
}
|
}
|
||||||
return $"({nodeId})";
|
return $"({nodeId})";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
// 연결된 노드 중 현재 노드가 아닌 것들만 필터링
|
// 연결된 노드 중 현재 노드가 아닌 것들만 필터링
|
||||||
var candidateNodes = allNodes.Where(n =>
|
var candidateNodes = allNodes.Where(n =>
|
||||||
connectedNodeIds.Contains(n.NodeId) && n.NodeId != currentNode.NodeId
|
connectedNodeIds.Contains(n.Id) && n.Id != currentNode.Id
|
||||||
).ToList();
|
).ToList();
|
||||||
|
|
||||||
if (candidateNodes.Count == 0)
|
if (candidateNodes.Count == 0)
|
||||||
@@ -88,7 +88,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
if (movementLength < 0.001f) // 거의 이동하지 않음
|
if (movementLength < 0.001f) // 거의 이동하지 않음
|
||||||
{
|
{
|
||||||
return candidateNodes[0].NodeId; // 첫 번째 연결 노드 반환
|
return candidateNodes[0].Id; // 첫 번째 연결 노드 반환
|
||||||
}
|
}
|
||||||
|
|
||||||
var normalizedMovement = new PointF(
|
var normalizedMovement = new PointF(
|
||||||
@@ -138,7 +138,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
|
|
||||||
// 가장 높은 점수를 가진 노드 반환
|
// 가장 높은 점수를 가진 노드 반환
|
||||||
var bestCandidate = scoredCandidates.OrderByDescending(x => x.score).First();
|
var bestCandidate = scoredCandidates.OrderByDescending(x => x.score).First();
|
||||||
return bestCandidate.node.NodeId;
|
return bestCandidate.node.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -40,13 +40,18 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// RFID Value
|
/// RFID Value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RfidId { get; set; }
|
public ushort RfidId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 해당 노드에서의 모터방향
|
/// 해당 노드에서의 모터방향
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AgvDirection MotorDirection { get; set; }
|
public AgvDirection MotorDirection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 해당 노드에서의 제한 속도
|
||||||
|
/// </summary>
|
||||||
|
public SpeedLevel Speed { get; set; } = SpeedLevel.M;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 마그넷 센서 방향 제어 (갈림길 처리용)
|
/// 마그넷 센서 방향 제어 (갈림길 처리용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -82,7 +87,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string SpecialActionDescription { get; set; }
|
public string SpecialActionDescription { get; set; }
|
||||||
|
|
||||||
public NodeMotorInfo(int seqno,string nodeId,string rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
||||||
{
|
{
|
||||||
seq = seqno;
|
seq = seqno;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
@@ -103,7 +108,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var result = $"{RfidId}[{NodeId}]:{MotorDirection}";
|
var result = $"R{RfidId}[N{NodeId}]:{MotorDirection}";
|
||||||
|
|
||||||
// 마그넷 방향이 직진이 아닌 경우 표시
|
// 마그넷 방향이 직진이 아닌 경우 표시
|
||||||
if (MagnetDirection != MagnetDirection.Straight)
|
if (MagnetDirection != MagnetDirection.Straight)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
List<MapNode> candidateNodes = new List<MapNode>();
|
List<MapNode> candidateNodes = new List<MapNode>();
|
||||||
if (prevDirection == direction)
|
if (prevDirection == direction)
|
||||||
{
|
{
|
||||||
candidateNodes = connectedMapNodes.Where(n => n.NodeId != prevNode.NodeId).ToList();
|
candidateNodes = connectedMapNodes.Where(n => n.Id != prevNode.Id).ToList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -112,9 +112,9 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\n[GetNextNodeByDirection] ========== 다음 노드 선택 시작 ==========");
|
$"\n[GetNextNodeByDirection] ========== 다음 노드 선택 시작 ==========");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 현재노드: {currentNode.RfidId}[{currentNode.NodeId}]({currentNode.Position.X:F1}, {currentNode.Position.Y:F1})");
|
$" 현재노드: {currentNode.RfidId}[{currentNode.Id}]({currentNode.Position.X:F1}, {currentNode.Position.Y:F1})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 이전노드: {prevNode.RfidId}[{prevNode.NodeId}]({prevNode.Position.X:F1}, {prevNode.Position.Y:F1})");
|
$" 이전노드: {prevNode.RfidId}[{prevNode.Id}]({prevNode.Position.X:F1}, {prevNode.Position.Y:F1})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 이동벡터: ({movementVector.X:F2}, {movementVector.Y:F2}) → 정규화: ({normalizedMovement.X:F3}, {normalizedMovement.Y:F3})");
|
$" 이동벡터: ({movementVector.X:F2}, {movementVector.Y:F2}) → 정규화: ({normalizedMovement.X:F3}, {normalizedMovement.Y:F3})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
@@ -159,7 +159,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\n [후보] {candidate.RfidId}[{candidate.NodeId}]({candidate.Position.X:F1}, {candidate.Position.Y:F1})");
|
$"\n [후보] {candidate.RfidId}[{candidate.Id}]({candidate.Position.X:F1}, {candidate.Position.Y:F1})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 벡터: ({toNextVector.X:F2}, {toNextVector.Y:F2}), 길이: {toNextLength:F2}");
|
$" 벡터: ({toNextVector.X:F2}, {toNextVector.Y:F2}), 길이: {toNextLength:F2}");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
@@ -204,7 +204,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\n 최종선택: {bestNode?.RfidId ?? "null"}[{bestNode?.NodeId ?? "null"}] (점수: {bestScore:F4})");
|
$"\n 최종선택: {bestNode?.RfidId ?? 0}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");
|
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");
|
||||||
|
|
||||||
@@ -445,15 +445,15 @@ namespace AGVNavigationCore.Utils
|
|||||||
// 선택 이유 생성
|
// 선택 이유 생성
|
||||||
if (prevMotorDirection.HasValue && direction == prevMotorDirection)
|
if (prevMotorDirection.HasValue && direction == prevMotorDirection)
|
||||||
{
|
{
|
||||||
reason = $"모터 방향 일관성 유지 ({direction}) → {candidate.NodeId}";
|
reason = $"모터 방향 일관성 유지 ({direction}) → {candidate.Id}";
|
||||||
}
|
}
|
||||||
else if (prevMotorDirection.HasValue)
|
else if (prevMotorDirection.HasValue)
|
||||||
{
|
{
|
||||||
reason = $"모터 방향 변경 ({prevMotorDirection} → {direction}) → {candidate.NodeId}";
|
reason = $"모터 방향 변경 ({prevMotorDirection} → {direction}) → {candidate.Id}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reason = $"방향 기반 선택 ({direction}) → {candidate.NodeId}";
|
reason = $"방향 기반 선택 ({direction}) → {candidate.Id}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,22 @@ namespace AGVNavigationCore.Utils
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DirectionalPathfinder 테스트 클래스
|
/// DirectionalPathfinder 테스트 클래스
|
||||||
/// NewMap.agvmap을 로드하여 방향별 다음 노드를 검증
|
/// NewMap.json 로드하여 방향별 다음 노드를 검증
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DirectionalPathfinderTest
|
public class DirectionalPathfinderTest
|
||||||
{
|
{
|
||||||
private List<MapNode> _allNodes;
|
private List<MapNode> _allNodes;
|
||||||
private Dictionary<string, MapNode> _nodesByRfidId;
|
private Dictionary<ushort, MapNode> _nodesByRfidId;
|
||||||
private AGVDirectionCalculator _calculator;
|
private AGVDirectionCalculator _calculator;
|
||||||
|
|
||||||
public DirectionalPathfinderTest()
|
public DirectionalPathfinderTest()
|
||||||
{
|
{
|
||||||
_nodesByRfidId = new Dictionary<string, MapNode>();
|
_nodesByRfidId = new Dictionary<ushort, MapNode>();
|
||||||
_calculator = new AGVDirectionCalculator();
|
_calculator = new AGVDirectionCalculator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// NewMap.agvmap 파일 로드
|
/// NewMap.json 파일 로드
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool LoadMapFile(string filePath)
|
public bool LoadMapFile(string filePath)
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
// RFID ID로 인덱싱
|
// RFID ID로 인덱싱
|
||||||
foreach (var node in _allNodes)
|
foreach (var node in _allNodes)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
if (node.HasRfid())
|
||||||
{
|
{
|
||||||
_nodesByRfidId[node.RfidId] = node;
|
_nodesByRfidId[node.RfidId] = node;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
|
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void TestDirectionalMovement(string previousRfidId, string currentRfidId, AgvDirection direction)
|
public void TestDirectionalMovement(ushort previousRfidId, ushort currentRfidId, AgvDirection direction)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\n========================================");
|
Console.WriteLine($"\n========================================");
|
||||||
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
|
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
|
||||||
@@ -90,8 +90,8 @@ namespace AGVNavigationCore.Utils
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"이전 노드: {previousNode.NodeId} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}");
|
Console.WriteLine($"이전 노드: {previousNode.Id} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}");
|
||||||
Console.WriteLine($"현재 노드: {currentNode.NodeId} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
||||||
Console.WriteLine($"이동 벡터: ({currentNode.Position.X - previousNode.Position.X}, " +
|
Console.WriteLine($"이동 벡터: ({currentNode.Position.X - previousNode.Position.X}, " +
|
||||||
$"{currentNode.Position.Y - previousNode.Position.Y})");
|
$"{currentNode.Position.Y - previousNode.Position.Y})");
|
||||||
|
|
||||||
@@ -111,10 +111,10 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 다음 노드 정보 출력
|
// 다음 노드 정보 출력
|
||||||
var nextNode = _allNodes.FirstOrDefault(n => n.NodeId == nextNodeId);
|
var nextNode = _allNodes.FirstOrDefault(n => n.Id == nextNodeId);
|
||||||
if (nextNode != null)
|
if (nextNode != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"✓ 다음 노드: {nextNode.NodeId} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}");
|
Console.WriteLine($"✓ 다음 노드: {nextNode.Id} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}");
|
||||||
Console.WriteLine($" ├─ 노드 타입: {GetNodeTypeName(nextNode.Type)}");
|
Console.WriteLine($" ├─ 노드 타입: {GetNodeTypeName(nextNode.Type)}");
|
||||||
Console.WriteLine($" └─ 연결된 노드: {string.Join(", ", nextNode.ConnectedNodes)}");
|
Console.WriteLine($" └─ 연결된 노드: {string.Join(", ", nextNode.ConnectedNodes)}");
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine("\n========== 모든 노드 정보 ==========");
|
Console.WriteLine("\n========== 모든 노드 정보 ==========");
|
||||||
foreach (var node in _allNodes.OrderBy(n => n.RfidId))
|
foreach (var node in _allNodes.OrderBy(n => n.RfidId))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{node.RfidId:D3} → {node.NodeId} ({GetNodeTypeName(node.Type)})");
|
Console.WriteLine($"{node.RfidId:D3} → {node.Id} ({GetNodeTypeName(node.Type)})");
|
||||||
Console.WriteLine($" 위치: {node.Position}, 연결: {string.Join(", ", node.ConnectedNodes)}");
|
Console.WriteLine($" 위치: {node.Position}, 연결: {string.Join(", ", node.ConnectedNodes)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 특정 RFID 노드의 상세 정보 출력
|
/// 특정 RFID 노드의 상세 정보 출력
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PrintNodeInfo(string rfidId)
|
public void PrintNodeInfo(ushort rfidId)
|
||||||
{
|
{
|
||||||
if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
|
if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
|
||||||
{
|
{
|
||||||
@@ -149,11 +149,12 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"\n========== RFID {rfidId} 상세 정보 ==========");
|
Console.WriteLine($"\n========== RFID {rfidId} 상세 정보 ==========");
|
||||||
Console.WriteLine($"노드 ID: {node.NodeId}");
|
Console.WriteLine($"노드 ID: {node.Id}");
|
||||||
Console.WriteLine($"이름: {node.Name}");
|
Console.WriteLine($"RFID: {node.RfidId}");
|
||||||
|
Console.WriteLine($"ALIAS: {node.AliasName}");
|
||||||
Console.WriteLine($"위치: {node.Position}");
|
Console.WriteLine($"위치: {node.Position}");
|
||||||
Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}");
|
Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}");
|
||||||
Console.WriteLine($"회전 가능: {node.CanRotate}");
|
Console.WriteLine($"TurnLeft/Right/교차로 : {(node.CanTurnLeft ? "O":"X")}/{(node.CanTurnRight ? "O" : "X")}/{(node.DisableCross ? "X" : "O")}");
|
||||||
Console.WriteLine($"활성: {node.IsActive}");
|
Console.WriteLine($"활성: {node.IsActive}");
|
||||||
Console.WriteLine($"연결된 노드:");
|
Console.WriteLine($"연결된 노드:");
|
||||||
|
|
||||||
@@ -165,7 +166,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
{
|
{
|
||||||
foreach (var connectedId in node.ConnectedNodes)
|
foreach (var connectedId in node.ConnectedNodes)
|
||||||
{
|
{
|
||||||
var connectedNode = _allNodes.FirstOrDefault(n => n.NodeId == connectedId);
|
var connectedNode = _allNodes.FirstOrDefault(n => n.Id == connectedId);
|
||||||
if (connectedNode != null)
|
if (connectedNode != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" → {connectedId} (RFID: {connectedNode.RfidId}) - 위치: {connectedNode.Position}");
|
Console.WriteLine($" → {connectedId} (RFID: {connectedNode.RfidId}) - 위치: {connectedNode.Position}");
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
return DockingValidationResult.CreateNotRequired();
|
return DockingValidationResult.CreateNotRequired();
|
||||||
}
|
}
|
||||||
if (pathResult.DetailedPath.Any() == false && pathResult.Path.Any() && pathResult.Path.Count == 2 &&
|
if (pathResult.DetailedPath.Any() == false && pathResult.Path.Any() && pathResult.Path.Count == 2 &&
|
||||||
pathResult.Path[0].NodeId == pathResult.Path[1].NodeId)
|
pathResult.Path[0].Id == pathResult.Path[1].Id)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 도킹 검증 불필요: 동일포인트");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 도킹 검증 불필요: 동일포인트");
|
||||||
return DockingValidationResult.CreateNotRequired();
|
return DockingValidationResult.CreateNotRequired();
|
||||||
@@ -44,7 +44,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
return DockingValidationResult.CreateNotRequired();
|
return DockingValidationResult.CreateNotRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 목적지 노드: {LastNode.NodeId} 타입:{LastNode.Type} ({(int)LastNode.Type})");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 목적지 노드: {LastNode.Id} 타입:{LastNode.Type} ({(int)LastNode.Type})");
|
||||||
|
|
||||||
//detail 경로 이동 예측 검증
|
//detail 경로 이동 예측 검증
|
||||||
for (int i = 0; i < pathResult.DetailedPath.Count - 1; i++)
|
for (int i = 0; i < pathResult.DetailedPath.Count - 1; i++)
|
||||||
@@ -52,8 +52,8 @@ namespace AGVNavigationCore.Utils
|
|||||||
var curNodeId = pathResult.DetailedPath[i].NodeId;
|
var curNodeId = pathResult.DetailedPath[i].NodeId;
|
||||||
var nextNodeId = pathResult.DetailedPath[i + 1].NodeId;
|
var nextNodeId = pathResult.DetailedPath[i + 1].NodeId;
|
||||||
|
|
||||||
var curNode = mapNodes?.FirstOrDefault(n => n.NodeId == curNodeId);
|
var curNode = mapNodes?.FirstOrDefault(n => n.Id == curNodeId);
|
||||||
var nextNode = mapNodes?.FirstOrDefault(n => n.NodeId == nextNodeId);
|
var nextNode = mapNodes?.FirstOrDefault(n => n.Id == nextNodeId);
|
||||||
|
|
||||||
if (curNode != null && nextNode != null)
|
if (curNode != null && nextNode != null)
|
||||||
{
|
{
|
||||||
@@ -67,7 +67,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var prevNodeId = pathResult.DetailedPath[i - 1].NodeId;
|
var prevNodeId = pathResult.DetailedPath[i - 1].NodeId;
|
||||||
prevNode = mapNodes?.FirstOrDefault(n => n.NodeId == prevNodeId);
|
prevNode = mapNodes?.FirstOrDefault(n => n.Id == prevNodeId);
|
||||||
prevDir = pathResult.DetailedPath[i - 1].MotorDirection;
|
prevDir = pathResult.DetailedPath[i - 1].MotorDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\n[ValidateDockingDirection] 경로 검증 단계 {i}:");
|
$"\n[ValidateDockingDirection] 경로 검증 단계 {i}:");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 이전→현재→다음: {prevNode.NodeId}({prevNode.RfidId}) → {curNode.NodeId}({curNode.RfidId}) → {nextNode.NodeId}({nextNode.RfidId})");
|
$" 이전→현재→다음: {prevNode.Id}({prevNode.RfidId}) → {curNode.Id}({curNode.RfidId}) → {nextNode.Id}({nextNode.RfidId})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" 현재 노드 위치: ({curNode.Position.X:F1}, {curNode.Position.Y:F1})");
|
$" 현재 노드 위치: ({curNode.Position.X:F1}, {curNode.Position.Y:F1})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
@@ -96,16 +96,16 @@ namespace AGVNavigationCore.Utils
|
|||||||
);
|
);
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" [예상] GetNextNodeByDirection 결과: {expectedNextNode?.NodeId ?? "null"}");
|
$" [예상] GetNextNodeByDirection 결과: {expectedNextNode?.Id ?? "null"}");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$" [실제] DetailedPath 다음 노드: {nextNode.RfidId}[{nextNode.NodeId}]");
|
$" [실제] DetailedPath 다음 노드: {nextNode.RfidId}[{nextNode.Id}]");
|
||||||
|
|
||||||
if (expectedNextNode != null && !expectedNextNode.NodeId.Equals(nextNode.NodeId))
|
if (expectedNextNode != null && !expectedNextNode.Id.Equals(nextNode.Id))
|
||||||
{
|
{
|
||||||
string error =
|
string error =
|
||||||
$"[DockingValidator] ⚠️ 경로 방향 불일치" +
|
$"[DockingValidator] ⚠️ 경로 방향 불일치" +
|
||||||
$"\n현재={curNode.RfidId}[{curNodeId}] 이전={prevNode.RfidId}[{(prevNode?.NodeId ?? string.Empty)}] " +
|
$"\n현재={curNode.RfidId}[{curNodeId}] 이전={prevNode.RfidId}[{(prevNode?.Id ?? string.Empty)}] " +
|
||||||
$"\n예상다음={expectedNextNode.RfidId}[{expectedNextNode.NodeId}] 실제다음={nextNode.RfidId}[{nextNodeId}]";
|
$"\n예상다음={expectedNextNode.RfidId}[{expectedNextNode.Id}] 실제다음={nextNode.RfidId}[{nextNodeId}]";
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[ValidateDockingDirection] ❌ 경로 방향 불일치 검출!");
|
$"[ValidateDockingDirection] ❌ 경로 방향 불일치 검출!");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
@@ -118,7 +118,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
$" 현재→실제: ({(nextNode.Position.X - curNode.Position.X):F2}, {(nextNode.Position.Y - curNode.Position.Y):F2})");
|
$" 현재→실제: ({(nextNode.Position.X - curNode.Position.X):F2}, {(nextNode.Position.Y - curNode.Position.Y):F2})");
|
||||||
Console.WriteLine($"[ValidateDockingDirection] 에러메시지: {error}");
|
Console.WriteLine($"[ValidateDockingDirection] 에러메시지: {error}");
|
||||||
return DockingValidationResult.CreateInvalid(
|
return DockingValidationResult.CreateInvalid(
|
||||||
LastNode.NodeId,
|
LastNode.Id,
|
||||||
LastNode.Type,
|
LastNode.Type,
|
||||||
pathResult.DetailedPath[i].MotorDirection,
|
pathResult.DetailedPath[i].MotorDirection,
|
||||||
pathResult.DetailedPath[i].MotorDirection,
|
pathResult.DetailedPath[i].MotorDirection,
|
||||||
@@ -150,12 +150,12 @@ namespace AGVNavigationCore.Utils
|
|||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 필요한 도킹 방향: {requiredDirection}");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 필요한 도킹 방향: {requiredDirection}");
|
||||||
|
|
||||||
var LastNodeInfo = pathResult.DetailedPath.Last();
|
var LastNodeInfo = pathResult.DetailedPath.Last();
|
||||||
if (LastNodeInfo.NodeId != LastNode.NodeId)
|
if (LastNodeInfo.NodeId != LastNode.Id)
|
||||||
{
|
{
|
||||||
string error = $"마지막 노드의 도킹방향과 경로정보의 노드ID 불일치: 필요={LastNode.NodeId}, 계산됨={LastNodeInfo.NodeId }";
|
string error = $"마지막 노드의 도킹방향과 경로정보의 노드ID 불일치: 필요={LastNode.Id}, 계산됨={LastNodeInfo.NodeId }";
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
|
||||||
return DockingValidationResult.CreateInvalid(
|
return DockingValidationResult.CreateInvalid(
|
||||||
LastNode.NodeId,
|
LastNode.Id,
|
||||||
LastNode.Type,
|
LastNode.Type,
|
||||||
requiredDirection,
|
requiredDirection,
|
||||||
LastNodeInfo.MotorDirection,
|
LastNodeInfo.MotorDirection,
|
||||||
@@ -167,7 +167,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
|
||||||
return DockingValidationResult.CreateValid(
|
return DockingValidationResult.CreateValid(
|
||||||
LastNode.NodeId,
|
LastNode.Id,
|
||||||
LastNode.Type,
|
LastNode.Type,
|
||||||
requiredDirection,
|
requiredDirection,
|
||||||
LastNodeInfo.MotorDirection);
|
LastNodeInfo.MotorDirection);
|
||||||
@@ -177,7 +177,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
string error = $"도킹 방향 불일치: 필요={GetDirectionText(requiredDirection)}, 계산됨={GetDirectionText(LastNodeInfo.MotorDirection)}";
|
string error = $"도킹 방향 불일치: 필요={GetDirectionText(requiredDirection)}, 계산됨={GetDirectionText(LastNodeInfo.MotorDirection)}";
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
|
||||||
return DockingValidationResult.CreateInvalid(
|
return DockingValidationResult.CreateInvalid(
|
||||||
LastNode.NodeId,
|
LastNode.Id,
|
||||||
LastNode.Type,
|
LastNode.Type,
|
||||||
requiredDirection,
|
requiredDirection,
|
||||||
LastNodeInfo.MotorDirection,
|
LastNodeInfo.MotorDirection,
|
||||||
@@ -264,7 +264,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
var deltaY = lastNode.Position.Y - secondLastNode.Position.Y;
|
var deltaY = lastNode.Position.Y - secondLastNode.Position.Y;
|
||||||
var distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
|
var distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[CalculateFinalDirection] 마지막 구간: {secondLastNode.NodeId} → {lastNode.NodeId}, 벡터: ({deltaX}, {deltaY}), 거리: {distance:F2}");
|
System.Diagnostics.Debug.WriteLine($"[CalculateFinalDirection] 마지막 구간: {secondLastNode.Id} → {lastNode.Id}, 벡터: ({deltaX}, {deltaY}), 거리: {distance:F2}");
|
||||||
|
|
||||||
// 이동 거리가 매우 작으면 현재 방향 유지
|
// 이동 거리가 매우 작으면 현재 방향 유지
|
||||||
if (distance < 1.0)
|
if (distance < 1.0)
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine("================================================\n");
|
Console.WriteLine("================================================\n");
|
||||||
|
|
||||||
// 테스트 노드 생성
|
// 테스트 노드 생성
|
||||||
var node001 = new MapNode { NodeId = "N001", RfidId = "001", Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } };
|
var node001 = new MapNode { Id = "N001", RfidId = 001, Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } };
|
||||||
var node002 = new MapNode { NodeId = "N002", RfidId = "002", Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } };
|
var node002 = new MapNode { Id = "N002", RfidId = 002, Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } };
|
||||||
var node003 = new MapNode { NodeId = "N003", RfidId = "003", Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } };
|
var node003 = new MapNode { Id = "N003", RfidId = 003, Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } };
|
||||||
var node004 = new MapNode { NodeId = "N004", RfidId = "004", Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } };
|
var node004 = new MapNode { Id = "N004", RfidId = 004, Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } };
|
||||||
|
|
||||||
var allNodes = new List<MapNode> { node001, node002, node003, node004 };
|
var allNodes = new List<MapNode> { node001, node002, node003, node004 };
|
||||||
|
|
||||||
@@ -114,8 +114,8 @@ namespace AGVNavigationCore.Utils
|
|||||||
AgvDirection motorDir = currentMotorDirection ?? direction;
|
AgvDirection motorDir = currentMotorDirection ?? direction;
|
||||||
|
|
||||||
Console.WriteLine($"설명: {description}");
|
Console.WriteLine($"설명: {description}");
|
||||||
Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId ?? "?"})");
|
Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId.ToString("0000") ?? "?"})");
|
||||||
Console.WriteLine($"현재 노드: {currentNode.NodeId} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
||||||
Console.WriteLine($"현재 모터 방향: {motorDir}");
|
Console.WriteLine($"현재 모터 방향: {motorDir}");
|
||||||
Console.WriteLine($"요청 방향: {direction}");
|
Console.WriteLine($"요청 방향: {direction}");
|
||||||
|
|
||||||
@@ -128,32 +128,32 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine($"이동 벡터: ({movementVector.X}, {movementVector.Y})");
|
Console.WriteLine($"이동 벡터: ({movementVector.X}, {movementVector.Y})");
|
||||||
|
|
||||||
// 각 후보 노드에 대한 점수 계산
|
// 각 후보 노드에 대한 점수 계산
|
||||||
Console.WriteLine($"\n현재 노드({currentNode.NodeId})의 ConnectedNodes: {string.Join(", ", currentNode.ConnectedNodes)}");
|
Console.WriteLine($"\n현재 노드({currentNode.Id})의 ConnectedNodes: {string.Join(", ", currentNode.ConnectedNodes)}");
|
||||||
Console.WriteLine($"가능한 다음 노드들:");
|
Console.WriteLine($"가능한 다음 노드들:");
|
||||||
|
|
||||||
var candidateNodes = allNodes.Where(n =>
|
var candidateNodes = allNodes.Where(n =>
|
||||||
currentNode.ConnectedNodes.Contains(n.NodeId) && n.NodeId != currentNode.NodeId
|
currentNode.ConnectedNodes.Contains(n.Id) && n.Id != currentNode.Id
|
||||||
).ToList();
|
).ToList();
|
||||||
|
|
||||||
foreach (var candidate in candidateNodes)
|
foreach (var candidate in candidateNodes)
|
||||||
{
|
{
|
||||||
var score = CalculateScoreAndPrint(movementVector, currentNode.Position, candidate, direction);
|
var score = CalculateScoreAndPrint(movementVector, currentNode.Position, candidate, direction);
|
||||||
string isExpected = (candidate.NodeId == expectedNextNode.NodeId) ? " ← 예상 노드" : "";
|
string isExpected = (candidate.Id == expectedNextNode.Id) ? " ← 예상 노드" : "";
|
||||||
Console.WriteLine($" {candidate.NodeId} (RFID: {candidate.RfidId}) - 위치: {candidate.Position} - 점수: {score:F1}{isExpected}");
|
Console.WriteLine($" {candidate.Id} (RFID: {candidate.RfidId}) - 위치: {candidate.Position} - 점수: {score:F1}{isExpected}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 최고 점수 노드 선택
|
// 최고 점수 노드 선택
|
||||||
var bestCandidate = GetBestCandidate(movementVector, currentNode.Position, candidateNodes, direction);
|
var bestCandidate = GetBestCandidate(movementVector, currentNode.Position, candidateNodes, direction);
|
||||||
|
|
||||||
Console.WriteLine($"\n✓ 선택된 노드: {bestCandidate.NodeId} (RFID: {bestCandidate.RfidId})");
|
Console.WriteLine($"\n✓ 선택된 노드: {bestCandidate.Id} (RFID: {bestCandidate.RfidId})");
|
||||||
|
|
||||||
if (bestCandidate.NodeId == expectedNextNode.NodeId)
|
if (bestCandidate.Id == expectedNextNode.Id)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"✅ 정답! ({expectedNodeIdStr})");
|
Console.WriteLine($"✅ 정답! ({expectedNodeIdStr})");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"❌ 오답! 예상: {expectedNextNode.NodeId}, 실제: {bestCandidate.NodeId}");
|
Console.WriteLine($"❌ 오답! 예상: {expectedNextNode.Id}, 실제: {bestCandidate.Id}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,14 +63,14 @@ namespace AGVNavigationCore.Utils
|
|||||||
// 이전 노드 제외 (되돌아가는 방향 제외)
|
// 이전 노드 제외 (되돌아가는 방향 제외)
|
||||||
if (previousNode != null)
|
if (previousNode != null)
|
||||||
{
|
{
|
||||||
nextNodes = nextNodes.Where(n => n.NodeId != previousNode.NodeId).ToList();
|
nextNodes = nextNodes.Where(n => n.Id != previousNode.Id).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextNodes.Count == 1)
|
if (nextNodes.Count == 1)
|
||||||
{
|
{
|
||||||
// 직선 경로: 다음 노드 방향으로 예측
|
// 직선 경로: 다음 노드 방향으로 예측
|
||||||
targetPosition = nextNodes.First().Position;
|
targetPosition = nextNodes.First().Position;
|
||||||
calculationMethod = $"전진 경로 예측 ({currentNode.NodeId}→{nextNodes.First().NodeId})";
|
calculationMethod = $"전진 경로 예측 ({currentNode.Id}→{nextNodes.First().Id})";
|
||||||
}
|
}
|
||||||
else if (nextNodes.Count > 1)
|
else if (nextNodes.Count > 1)
|
||||||
{
|
{
|
||||||
@@ -268,7 +268,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
|
|
||||||
foreach (var nodeId in currentNode.ConnectedNodes)
|
foreach (var nodeId in currentNode.ConnectedNodes)
|
||||||
{
|
{
|
||||||
var connectedNode = mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
|
var connectedNode = mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (connectedNode != null)
|
if (connectedNode != null)
|
||||||
{
|
{
|
||||||
connectedNodes.Add(connectedNode);
|
connectedNodes.Add(connectedNode);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
{
|
{
|
||||||
public void RunTests()
|
public void RunTests()
|
||||||
{
|
{
|
||||||
string mapFilePath = @"C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.agvmap";
|
string mapFilePath = @"C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.json";
|
||||||
|
|
||||||
var tester = new DirectionalPathfinderTest();
|
var tester = new DirectionalPathfinderTest();
|
||||||
|
|
||||||
@@ -29,26 +29,26 @@ namespace AGVNavigationCore.Utils
|
|||||||
tester.PrintAllNodes();
|
tester.PrintAllNodes();
|
||||||
|
|
||||||
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
|
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
|
||||||
tester.PrintNodeInfo("001");
|
tester.PrintNodeInfo(001);
|
||||||
tester.PrintNodeInfo("002");
|
tester.PrintNodeInfo(002);
|
||||||
tester.TestDirectionalMovement("001", "002", AgvDirection.Forward);
|
tester.TestDirectionalMovement(001, 002, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
|
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
|
||||||
tester.TestDirectionalMovement("002", "001", AgvDirection.Backward);
|
tester.TestDirectionalMovement(002, 001, AgvDirection.Backward);
|
||||||
|
|
||||||
// 테스트 시나리오 3: 002 → 003 → Forward
|
// 테스트 시나리오 3: 002 → 003 → Forward
|
||||||
tester.PrintNodeInfo("003");
|
tester.PrintNodeInfo(003);
|
||||||
tester.TestDirectionalMovement("002", "003", AgvDirection.Forward);
|
tester.TestDirectionalMovement(002, 003, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 4: 003 → 004 → Forward
|
// 테스트 시나리오 4: 003 → 004 → Forward
|
||||||
tester.PrintNodeInfo("004");
|
tester.PrintNodeInfo(004);
|
||||||
tester.TestDirectionalMovement("003", "004", AgvDirection.Forward);
|
tester.TestDirectionalMovement(003, 004, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 5: 003 → 004 → Right (030 기대)
|
// 테스트 시나리오 5: 003 → 004 → Right (030 기대)
|
||||||
tester.TestDirectionalMovement("003", "004", AgvDirection.Right);
|
tester.TestDirectionalMovement(003, 004, AgvDirection.Right);
|
||||||
|
|
||||||
// 테스트 시나리오 6: 004 → 003 → Backward
|
// 테스트 시나리오 6: 004 → 003 → Backward
|
||||||
tester.TestDirectionalMovement("004", "003", AgvDirection.Backward);
|
tester.TestDirectionalMovement(004, 003, AgvDirection.Backward);
|
||||||
|
|
||||||
Console.WriteLine("\n\n=== 테스트 완료 ===");
|
Console.WriteLine("\n\n=== 테스트 완료 ===");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net48" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
|
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\Simulator\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,21 @@
|
|||||||
{
|
{
|
||||||
"Nodes": [
|
"Nodes": [
|
||||||
{
|
{
|
||||||
"NodeId": "N001",
|
"Id": "N001",
|
||||||
"Name": "UNLOADER",
|
"Name": "UNLOADER",
|
||||||
"Position": "65, 229",
|
"Position": "65, 229",
|
||||||
"Type": 2,
|
"Type": 2,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N002"
|
"N002"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:44.9548285+09:00",
|
"CreatedDate": "2025-09-11T08:34:44.9548285+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:03.1730939+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Red",
|
"DisplayColor": "Red",
|
||||||
"RfidId": "0001",
|
"RfidId": "0001",
|
||||||
@@ -38,20 +40,22 @@
|
|||||||
"DisplayText": "N001 - UNLOADER - [0001]"
|
"DisplayText": "N001 - UNLOADER - [0001]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N002",
|
"Id": "N002",
|
||||||
"Name": "N002",
|
"Name": "N002",
|
||||||
"Position": "190, 230",
|
"Position": "190, 230",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N003",
|
"N003",
|
||||||
"N001"
|
"N001"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:48.2957516+09:00",
|
"CreatedDate": "2025-09-11T08:34:48.2957516+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:05.9083675+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0002",
|
"RfidId": "0002",
|
||||||
@@ -76,20 +80,22 @@
|
|||||||
"DisplayText": "N002 - N002 - [0002]"
|
"DisplayText": "N002 - N002 - [0002]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N003",
|
"Id": "N003",
|
||||||
"Name": "N003",
|
"Name": "N003",
|
||||||
"Position": "296, 266",
|
"Position": "296, 266",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N004",
|
"N004",
|
||||||
"N002"
|
"N002"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:49.2226656+09:00",
|
"CreatedDate": "2025-09-11T08:34:49.2226656+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:07.7966472+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0003",
|
"RfidId": "0003",
|
||||||
@@ -114,10 +120,11 @@
|
|||||||
"DisplayText": "N003 - N003 - [0003]"
|
"DisplayText": "N003 - N003 - [0003]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N004",
|
"Id": "N004",
|
||||||
"Name": "N004",
|
"Name": "N004",
|
||||||
"Position": "388, 330",
|
"Position": "388, 330",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N022",
|
"N022",
|
||||||
@@ -125,11 +132,12 @@
|
|||||||
"N011",
|
"N011",
|
||||||
"N003"
|
"N003"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:50.1681027+09:00",
|
"CreatedDate": "2025-09-11T08:34:50.1681027+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:09.4014966+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0004",
|
"RfidId": "0004",
|
||||||
@@ -154,20 +162,22 @@
|
|||||||
"DisplayText": "N004 - N004 - [0004]"
|
"DisplayText": "N004 - N004 - [0004]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N006",
|
"Id": "N006",
|
||||||
"Name": "N006",
|
"Name": "N006",
|
||||||
"Position": "530, 220",
|
"Position": "530, 220",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N007",
|
"N007",
|
||||||
"N022"
|
"N022"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:51.1111368+09:00",
|
"CreatedDate": "2025-09-11T08:34:51.1111368+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:49.3718611+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0013",
|
"RfidId": "0013",
|
||||||
@@ -192,20 +202,22 @@
|
|||||||
"DisplayText": "N006 - N006 - [0013]"
|
"DisplayText": "N006 - N006 - [0013]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N007",
|
"Id": "N007",
|
||||||
"Name": "N007",
|
"Name": "N007",
|
||||||
"Position": "589, 184",
|
"Position": "589, 184",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N019",
|
"N019",
|
||||||
"N006"
|
"N006"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:51.9266982+09:00",
|
"CreatedDate": "2025-09-11T08:34:51.9266982+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:51.4185821+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0014",
|
"RfidId": "0014",
|
||||||
@@ -230,20 +242,22 @@
|
|||||||
"DisplayText": "N007 - N007 - [0014]"
|
"DisplayText": "N007 - N007 - [0014]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N008",
|
"Id": "N008",
|
||||||
"Name": "N008",
|
"Name": "N008",
|
||||||
"Position": "282, 452",
|
"Position": "282, 452",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N009",
|
"N009",
|
||||||
"N031"
|
"N031"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:53.9595825+09:00",
|
"CreatedDate": "2025-09-11T08:34:53.9595825+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:13.626916+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0009",
|
"RfidId": "0009",
|
||||||
@@ -268,20 +282,22 @@
|
|||||||
"DisplayText": "N008 - N008 - [0009]"
|
"DisplayText": "N008 - N008 - [0009]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N009",
|
"Id": "N009",
|
||||||
"Name": "N009",
|
"Name": "N009",
|
||||||
"Position": "183, 465",
|
"Position": "183, 465",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N010",
|
"N010",
|
||||||
"N008"
|
"N008"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:54.5035702+09:00",
|
"CreatedDate": "2025-09-11T08:34:54.5035702+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:15.2395639+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0010",
|
"RfidId": "0010",
|
||||||
@@ -306,19 +322,21 @@
|
|||||||
"DisplayText": "N009 - N009 - [0010]"
|
"DisplayText": "N009 - N009 - [0010]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N010",
|
"Id": "N010",
|
||||||
"Name": "TOPS",
|
"Name": "TOPS",
|
||||||
"Position": "52, 466",
|
"Position": "52, 466",
|
||||||
"Type": 2,
|
"Type": 3,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N009"
|
"N009"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:55.0563237+09:00",
|
"CreatedDate": "2025-09-11T08:34:55.0563237+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:17.075895+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Red",
|
"DisplayColor": "Red",
|
||||||
"RfidId": "0011",
|
"RfidId": "0011",
|
||||||
@@ -343,21 +361,23 @@
|
|||||||
"DisplayText": "N010 - TOPS - [0011]"
|
"DisplayText": "N010 - TOPS - [0011]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N011",
|
"Id": "N011",
|
||||||
"Name": "N011",
|
"Name": "N011",
|
||||||
"Position": "481, 399",
|
"Position": "481, 399",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N012",
|
"N012",
|
||||||
"N015",
|
"N015",
|
||||||
"N004"
|
"N004"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:55.8875335+09:00",
|
"CreatedDate": "2025-09-11T08:34:55.8875335+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:30.5451017+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0005",
|
"RfidId": "0005",
|
||||||
@@ -382,20 +402,22 @@
|
|||||||
"DisplayText": "N011 - N011 - [0005]"
|
"DisplayText": "N011 - N011 - [0005]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N012",
|
"Id": "N012",
|
||||||
"Name": "N012",
|
"Name": "N012",
|
||||||
"Position": "559, 464",
|
"Position": "559, 464",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N013",
|
"N013",
|
||||||
"N011"
|
"N011"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:56.3678144+09:00",
|
"CreatedDate": "2025-09-11T08:34:56.3678144+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:32.3566504+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0006",
|
"RfidId": "0006",
|
||||||
@@ -420,20 +442,22 @@
|
|||||||
"DisplayText": "N012 - N012 - [0006]"
|
"DisplayText": "N012 - N012 - [0006]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N013",
|
"Id": "N013",
|
||||||
"Name": "N013",
|
"Name": "N013",
|
||||||
"Position": "640, 513",
|
"Position": "640, 513",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N014",
|
"N014",
|
||||||
"N012"
|
"N012"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:56.8390845+09:00",
|
"CreatedDate": "2025-09-11T08:34:56.8390845+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:34.1608589+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0007",
|
"RfidId": "0007",
|
||||||
@@ -458,19 +482,21 @@
|
|||||||
"DisplayText": "N013 - N013 - [0007]"
|
"DisplayText": "N013 - N013 - [0007]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N014",
|
"Id": "N014",
|
||||||
"Name": "LOADER",
|
"Name": "LOADER",
|
||||||
"Position": "728, 573",
|
"Position": "728, 573",
|
||||||
"Type": 2,
|
"Type": 1,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N013"
|
"N013"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:34:57.2549726+09:00",
|
"CreatedDate": "2025-09-11T08:34:57.2549726+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:36.2103863+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Red",
|
"DisplayColor": "Red",
|
||||||
"RfidId": "0008",
|
"RfidId": "0008",
|
||||||
@@ -495,19 +521,21 @@
|
|||||||
"DisplayText": "N014 - LOADER - [0008]"
|
"DisplayText": "N014 - LOADER - [0008]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N019",
|
"Id": "N019",
|
||||||
"Name": "CHARGER #2",
|
"Name": "CHARGER #2",
|
||||||
"Position": "679, 199",
|
"Position": "679, 199",
|
||||||
"Type": 3,
|
"Type": 5,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 1,
|
"DockDirection": 1,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N007"
|
"N007"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:35:56.5359098+09:00",
|
"CreatedDate": "2025-09-11T08:35:56.5359098+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:53.4706808+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Magenta",
|
"DisplayColor": "Magenta",
|
||||||
"RfidId": "0015",
|
"RfidId": "0015",
|
||||||
@@ -532,21 +560,23 @@
|
|||||||
"DisplayText": "N019 - CHARGER #2 - [0015]"
|
"DisplayText": "N019 - CHARGER #2 - [0015]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N022",
|
"Id": "N022",
|
||||||
"Name": "N022",
|
"Name": "N022",
|
||||||
"Position": "461, 267",
|
"Position": "461, 267",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N023",
|
"N023",
|
||||||
"N004",
|
"N004",
|
||||||
"N006"
|
"N006"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T08:36:48.0311551+09:00",
|
"CreatedDate": "2025-09-11T08:36:48.0311551+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:47.1279261+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0012",
|
"RfidId": "0012",
|
||||||
@@ -571,20 +601,22 @@
|
|||||||
"DisplayText": "N022 - N022 - [0012]"
|
"DisplayText": "N022 - N022 - [0012]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N023",
|
"Id": "N023",
|
||||||
"Name": "N023",
|
"Name": "N023",
|
||||||
"Position": "418, 206",
|
"Position": "418, 206",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N024",
|
"N024",
|
||||||
"N022"
|
"N022"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T09:41:36.8738794+09:00",
|
"CreatedDate": "2025-09-11T09:41:36.8738794+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:55:02.2993636+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0016",
|
"RfidId": "0016",
|
||||||
@@ -609,20 +641,22 @@
|
|||||||
"DisplayText": "N023 - N023 - [0016]"
|
"DisplayText": "N023 - N023 - [0016]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N024",
|
"Id": "N024",
|
||||||
"Name": "N024",
|
"Name": "N024",
|
||||||
"Position": "476, 141",
|
"Position": "476, 141",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N025",
|
"N025",
|
||||||
"N023"
|
"N023"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T09:41:37.4551853+09:00",
|
"CreatedDate": "2025-09-11T09:41:37.4551853+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:55:00.0899323+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0017",
|
"RfidId": "0017",
|
||||||
@@ -647,20 +681,22 @@
|
|||||||
"DisplayText": "N024 - N024 - [0017]"
|
"DisplayText": "N024 - N024 - [0017]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N025",
|
"Id": "N025",
|
||||||
"Name": "N025",
|
"Name": "N025",
|
||||||
"Position": "548, 99",
|
"Position": "548, 99",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N026",
|
"N026",
|
||||||
"N024"
|
"N024"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T09:41:38.0142374+09:00",
|
"CreatedDate": "2025-09-11T09:41:38.0142374+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:57.8459375+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0018",
|
"RfidId": "0018",
|
||||||
@@ -685,19 +721,21 @@
|
|||||||
"DisplayText": "N025 - N025 - [0018]"
|
"DisplayText": "N025 - N025 - [0018]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N026",
|
"Id": "N026",
|
||||||
"Name": "CHARGER #1",
|
"Name": "CHARGER #1",
|
||||||
"Position": "670, 88",
|
"Position": "670, 88",
|
||||||
"Type": 3,
|
"Type": 5,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 1,
|
"DockDirection": 1,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N025"
|
"N025"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T09:41:38.5834487+09:00",
|
"CreatedDate": "2025-09-11T09:41:38.5834487+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:55.6038784+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Magenta",
|
"DisplayColor": "Magenta",
|
||||||
"RfidId": "0019",
|
"RfidId": "0019",
|
||||||
@@ -722,17 +760,19 @@
|
|||||||
"DisplayText": "N026 - CHARGER #1 - [0019]"
|
"DisplayText": "N026 - CHARGER #1 - [0019]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "LBL001",
|
"Id": "LBL001",
|
||||||
"Name": "Amkor Technology Korea",
|
"Name": "Amkor Technology Korea",
|
||||||
"Position": "183, 103",
|
"Position": "183, 103",
|
||||||
"Type": 4,
|
"Type": 6,
|
||||||
"DockDirection": 0,
|
"CanDocking": false,
|
||||||
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [],
|
"ConnectedNodes": [],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T11:08:22.4048927+09:00",
|
"CreatedDate": "2025-09-11T11:08:22.4048927+09:00",
|
||||||
"ModifiedDate": "2025-10-30T17:14:35.0184087+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Purple",
|
"DisplayColor": "Purple",
|
||||||
"RfidId": "",
|
"RfidId": "",
|
||||||
@@ -757,17 +797,19 @@
|
|||||||
"DisplayText": "LBL001 - Amkor Technology Korea"
|
"DisplayText": "LBL001 - Amkor Technology Korea"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "IMG001",
|
"Id": "IMG001",
|
||||||
"Name": "logo",
|
"Name": "logo",
|
||||||
"Position": "633, 310",
|
"Position": "633, 310",
|
||||||
"Type": 5,
|
"Type": 7,
|
||||||
"DockDirection": 0,
|
"CanDocking": false,
|
||||||
|
"DockDirection": 1,
|
||||||
"ConnectedNodes": [],
|
"ConnectedNodes": [],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-11T11:08:44.7897541+09:00",
|
"CreatedDate": "2025-09-11T11:08:44.7897541+09:00",
|
||||||
"ModifiedDate": "2025-10-23T12:21:16.7786615+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Brown",
|
"DisplayColor": "Brown",
|
||||||
"RfidId": "",
|
"RfidId": "",
|
||||||
@@ -792,20 +834,22 @@
|
|||||||
"DisplayText": "IMG001 - logo"
|
"DisplayText": "IMG001 - logo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N015",
|
"Id": "N015",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "448, 476",
|
"Position": "448, 476",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N016",
|
"N016",
|
||||||
"N011"
|
"N011"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:47.8065756+09:00",
|
"CreatedDate": "2025-09-12T17:22:47.8065756+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:28.0157957+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0037",
|
"RfidId": "0037",
|
||||||
@@ -830,20 +874,22 @@
|
|||||||
"DisplayText": "N015 - [0037]"
|
"DisplayText": "N015 - [0037]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N016",
|
"Id": "N016",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "425, 524",
|
"Position": "425, 524",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N017",
|
"N017",
|
||||||
"N015"
|
"N015"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:48.6628848+09:00",
|
"CreatedDate": "2025-09-12T17:22:48.6628848+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:26.4584771+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0036",
|
"RfidId": "0036",
|
||||||
@@ -868,20 +914,22 @@
|
|||||||
"DisplayText": "N016 - [0036]"
|
"DisplayText": "N016 - [0036]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N017",
|
"Id": "N017",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "389, 559",
|
"Position": "389, 559",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N018",
|
"N018",
|
||||||
"N016"
|
"N016"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:49.8138877+09:00",
|
"CreatedDate": "2025-09-12T17:22:49.8138877+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:24.9652264+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0035",
|
"RfidId": "0035",
|
||||||
@@ -906,21 +954,23 @@
|
|||||||
"DisplayText": "N017 - [0035]"
|
"DisplayText": "N017 - [0035]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N018",
|
"Id": "N018",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "315, 562",
|
"Position": "315, 562",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N030",
|
"N030",
|
||||||
"N017",
|
"N017",
|
||||||
"N005"
|
"N005"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:50.6790623+09:00",
|
"CreatedDate": "2025-09-12T17:22:50.6790623+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:23.246666+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0034",
|
"RfidId": "0034",
|
||||||
@@ -945,21 +995,23 @@
|
|||||||
"DisplayText": "N018 - [0034]"
|
"DisplayText": "N018 - [0034]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N005",
|
"Id": "N005",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "227, 560",
|
"Position": "227, 560",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N020",
|
"N020",
|
||||||
"N029",
|
"N029",
|
||||||
"N018"
|
"N018"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:51.5267199+09:00",
|
"CreatedDate": "2025-09-12T17:22:51.5267199+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:21.6078399+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0033",
|
"RfidId": "0033",
|
||||||
@@ -984,21 +1036,23 @@
|
|||||||
"DisplayText": "N005 - [0033]"
|
"DisplayText": "N005 - [0033]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N020",
|
"Id": "N020",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "142, 557",
|
"Position": "142, 557",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N021",
|
"N021",
|
||||||
"N028",
|
"N028",
|
||||||
"N005"
|
"N005"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:52.3666114+09:00",
|
"CreatedDate": "2025-09-12T17:22:52.3666114+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:20.2707567+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0032",
|
"RfidId": "0032",
|
||||||
@@ -1023,20 +1077,22 @@
|
|||||||
"DisplayText": "N020 - [0032]"
|
"DisplayText": "N020 - [0032]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N021",
|
"Id": "N021",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "60, 559",
|
"Position": "60, 559",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N027",
|
"N027",
|
||||||
"N020"
|
"N020"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:53.0958619+09:00",
|
"CreatedDate": "2025-09-12T17:22:53.0958619+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:18.362104+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0031",
|
"RfidId": "0031",
|
||||||
@@ -1061,19 +1117,21 @@
|
|||||||
"DisplayText": "N021 - [0031]"
|
"DisplayText": "N021 - [0031]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N027",
|
"Id": "N027",
|
||||||
"Name": "BUF1",
|
"Name": "BUF1",
|
||||||
"Position": "61, 645",
|
"Position": "61, 645",
|
||||||
"Type": 2,
|
"Type": 4,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N021"
|
"N021"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:54.7345704+09:00",
|
"CreatedDate": "2025-09-12T17:22:54.7345704+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:43.1573737+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Green",
|
"DisplayColor": "Green",
|
||||||
"RfidId": "0041",
|
"RfidId": "0041",
|
||||||
@@ -1098,19 +1156,21 @@
|
|||||||
"DisplayText": "N027 - BUF1 - [0041]"
|
"DisplayText": "N027 - BUF1 - [0041]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N028",
|
"Id": "N028",
|
||||||
"Name": "BUF2",
|
"Name": "BUF2",
|
||||||
"Position": "141, 643",
|
"Position": "141, 643",
|
||||||
"Type": 2,
|
"Type": 4,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N020"
|
"N020"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:55.5263512+09:00",
|
"CreatedDate": "2025-09-12T17:22:55.5263512+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:41.5219283+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Green",
|
"DisplayColor": "Green",
|
||||||
"RfidId": "0040",
|
"RfidId": "0040",
|
||||||
@@ -1135,19 +1195,21 @@
|
|||||||
"DisplayText": "N028 - BUF2 - [0040]"
|
"DisplayText": "N028 - BUF2 - [0040]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N029",
|
"Id": "N029",
|
||||||
"Name": "BUF3",
|
"Name": "BUF3",
|
||||||
"Position": "229, 638",
|
"Position": "229, 638",
|
||||||
"Type": 2,
|
"Type": 4,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N005"
|
"N005"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:56.6623294+09:00",
|
"CreatedDate": "2025-09-12T17:22:56.6623294+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:39.9945926+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Green",
|
"DisplayColor": "Green",
|
||||||
"RfidId": "0039",
|
"RfidId": "0039",
|
||||||
@@ -1172,19 +1234,21 @@
|
|||||||
"DisplayText": "N029 - BUF3 - [0039]"
|
"DisplayText": "N029 - BUF3 - [0039]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N030",
|
"Id": "N030",
|
||||||
"Name": "BUF4",
|
"Name": "BUF4",
|
||||||
"Position": "316, 638",
|
"Position": "316, 638",
|
||||||
"Type": 2,
|
"Type": 4,
|
||||||
|
"CanDocking": true,
|
||||||
"DockDirection": 2,
|
"DockDirection": 2,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N018"
|
"N018"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": true,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-12T17:22:57.5510908+09:00",
|
"CreatedDate": "2025-09-12T17:22:57.5510908+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:38.3326835+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Green",
|
"DisplayColor": "Green",
|
||||||
"RfidId": "0038",
|
"RfidId": "0038",
|
||||||
@@ -1209,20 +1273,22 @@
|
|||||||
"DisplayText": "N030 - BUF4 - [0038]"
|
"DisplayText": "N030 - BUF4 - [0038]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NodeId": "N031",
|
"Id": "N031",
|
||||||
"Name": "",
|
"Name": "",
|
||||||
"Position": "337, 397",
|
"Position": "337, 397",
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
|
"CanDocking": false,
|
||||||
"DockDirection": 0,
|
"DockDirection": 0,
|
||||||
"ConnectedNodes": [
|
"ConnectedNodes": [
|
||||||
"N004",
|
"N004",
|
||||||
"N008"
|
"N008"
|
||||||
],
|
],
|
||||||
"CanRotate": false,
|
"CanTurnLeft": true,
|
||||||
"StationId": "",
|
"CanTurnRight": true,
|
||||||
"StationType": null,
|
"DisableCross": false,
|
||||||
|
"NodeAlias": "",
|
||||||
"CreatedDate": "2025-09-15T11:18:40.5366059+09:00",
|
"CreatedDate": "2025-09-15T11:18:40.5366059+09:00",
|
||||||
"ModifiedDate": "2025-12-08T11:54:12.0214781+09:00",
|
"ModifiedDate": "2025-12-09T08:41:57.1293017+09:00",
|
||||||
"IsActive": true,
|
"IsActive": true,
|
||||||
"DisplayColor": "Cyan",
|
"DisplayColor": "Cyan",
|
||||||
"RfidId": "0030",
|
"RfidId": "0030",
|
||||||
@@ -1251,6 +1317,6 @@
|
|||||||
"BackgroundColorArgb": -14671840,
|
"BackgroundColorArgb": -14671840,
|
||||||
"ShowGrid": false
|
"ShowGrid": false
|
||||||
},
|
},
|
||||||
"CreatedDate": "2025-12-08T11:55:04.9060276+09:00",
|
"CreatedDate": "2025-12-09T17:00:29.7458276+09:00",
|
||||||
"Version": "1.1"
|
"Version": "1.1"
|
||||||
}
|
}
|
||||||
1219
Cs_HMI/Data/NewMap.json
Normal file
1219
Cs_HMI/Data/NewMap.json
Normal file
File diff suppressed because it is too large
Load Diff
1135
Cs_HMI/Data/NewMap_2.json
Normal file
1135
Cs_HMI/Data/NewMap_2.json
Normal file
File diff suppressed because it is too large
Load Diff
1005
Cs_HMI/Data/NewMap_3.json
Normal file
1005
Cs_HMI/Data/NewMap_3.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@
|
|||||||
<DebugType>Full</DebugType>
|
<DebugType>Full</DebugType>
|
||||||
<Optimize>False</Optimize>
|
<Optimize>False</Optimize>
|
||||||
<OutputPath>..\..\..\..\..\Amkor\AGV4\</OutputPath>
|
<OutputPath>..\..\..\..\..\Amkor\AGV4\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;NOSPEECH</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="arCommUtil, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="arCommUtil, Version=25.11.25.2000, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\DLL\arCommUtil.dll</HintPath>
|
<HintPath>..\DLL\arCommUtil.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@@ -116,8 +116,7 @@
|
|||||||
<HintPath>C:\Program Files (x86)\Microsoft SDKs\Speech\v11.0\Assembly\Microsoft.Speech.dll</HintPath>
|
<HintPath>C:\Program Files (x86)\Microsoft SDKs\Speech\v11.0\Assembly\Microsoft.Speech.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
@@ -187,7 +186,10 @@
|
|||||||
<Compile Include="Device\BMS.cs" />
|
<Compile Include="Device\BMS.cs" />
|
||||||
<Compile Include="Device\BMSInformationEventArgs.cs" />
|
<Compile Include="Device\BMSInformationEventArgs.cs" />
|
||||||
<Compile Include="Device\CFlag.cs" />
|
<Compile Include="Device\CFlag.cs" />
|
||||||
<Compile Include="Device\xbee.cs" />
|
<Compile Include="Device\BMSSerialComm.cs" />
|
||||||
|
<Compile Include="Device\xbee.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Device\Socket.cs" />
|
<Compile Include="Device\Socket.cs" />
|
||||||
<Compile Include="Dialog\fCounter.cs">
|
<Compile Include="Dialog\fCounter.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
@@ -195,6 +197,12 @@
|
|||||||
<Compile Include="Dialog\fCounter.Designer.cs">
|
<Compile Include="Dialog\fCounter.Designer.cs">
|
||||||
<DependentUpon>fCounter.cs</DependentUpon>
|
<DependentUpon>fCounter.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Dialog\fXbeeSetting.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Dialog\fXbeeSetting.Designer.cs">
|
||||||
|
<DependentUpon>fXbeeSetting.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Dialog\fStateMachineDebug.cs">
|
<Compile Include="Dialog\fStateMachineDebug.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -225,9 +233,6 @@
|
|||||||
<Compile Include="Dialog\fSystem.Designer.cs">
|
<Compile Include="Dialog\fSystem.Designer.cs">
|
||||||
<DependentUpon>fSystem.cs</DependentUpon>
|
<DependentUpon>fSystem.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Dialog\DriveDetector.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Dialog\fVolume.cs">
|
<Compile Include="Dialog\fVolume.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -284,6 +289,33 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="PUB.cs" />
|
<Compile Include="PUB.cs" />
|
||||||
<Compile Include="CSetting.cs" />
|
<Compile Include="CSetting.cs" />
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_CLEANER_IN.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_CLEANER_OUT.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_LOADER_IN.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_LOADER_OUT.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_UNLOADER_IN.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_UNLOADER_OUT.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_Util.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_BUFFER_IN.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="StateMachine\Step\_SM_RUN_BUFFER_OUT.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="StateMachine\Step\_SM_RUN_CHGOFF.cs">
|
<Compile Include="StateMachine\Step\_SM_RUN_CHGOFF.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -305,12 +337,6 @@
|
|||||||
<Compile Include="StateMachine\Step\_SM_RUN_GOHOME.cs">
|
<Compile Include="StateMachine\Step\_SM_RUN_GOHOME.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="StateMachine\Step\_SM_RUN_GOUP.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="StateMachine\Step\_SM_RUN_GODOWN.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="StateMachine\Step\_SM_RUN_SYNC.cs">
|
<Compile Include="StateMachine\Step\_SM_RUN_SYNC.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -341,9 +367,6 @@
|
|||||||
<Compile Include="StateMachine\_SPS.cs">
|
<Compile Include="StateMachine\_SPS.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Device\_DeviceManagement.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="StateMachine\_Loop.cs">
|
<Compile Include="StateMachine\_Loop.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -401,6 +424,9 @@
|
|||||||
<EmbeddedResource Include="Dialog\fCounter.resx">
|
<EmbeddedResource Include="Dialog\fCounter.resx">
|
||||||
<DependentUpon>fCounter.cs</DependentUpon>
|
<DependentUpon>fCounter.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Dialog\fXbeeSetting.resx">
|
||||||
|
<DependentUpon>fXbeeSetting.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialog\fUpdateForm.resx">
|
<EmbeddedResource Include="Dialog\fUpdateForm.resx">
|
||||||
<DependentUpon>fUpdateForm.cs</DependentUpon>
|
<DependentUpon>fUpdateForm.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
|||||||
@@ -222,8 +222,6 @@ namespace Project
|
|||||||
|
|
||||||
#region "Charge"
|
#region "Charge"
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public int chargerpos { get; set; }
|
|
||||||
[Browsable(false)]
|
|
||||||
public int ChargetWaitSec { get; set; }
|
public int ChargetWaitSec { get; set; }
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public int ChargeEmergencyLevel { get; set; }
|
public int ChargeEmergencyLevel { get; set; }
|
||||||
@@ -240,29 +238,27 @@ namespace Project
|
|||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public int ChargeSearchTime { get; set; }
|
public int ChargeSearchTime { get; set; }
|
||||||
|
|
||||||
|
[Category("Charge"), DisplayName("Low Battery Limit"), Description("배터리 부족 경고 알림 기준 (%)")]
|
||||||
|
public int BatteryLimit_Low { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "AGV"
|
#region "AGV"
|
||||||
|
|
||||||
public bool AutoModeOffAndClearPosition { get; set; }
|
public bool AutoModeOffAndClearPosition { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public UInt16 GDSValue { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string musicfile { get; set; }
|
public string musicfile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// FVI로 가능 방향이 Backward 인가?
|
|
||||||
/// Forward 방향이라면 false 를 한다
|
|
||||||
/// </summary>
|
|
||||||
[Browsable(false)]
|
|
||||||
public Boolean AGV_Direction_FVI_Backward { get; set; }
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public Boolean Enable_Speak { get; set; }
|
public Boolean Enable_Speak { get; set; }
|
||||||
|
|
||||||
//public Boolean Disable_BMS { get; set; }
|
|
||||||
//public Boolean Log_BMS_Tx { get; set; }
|
|
||||||
//public Boolean Log_BMS_Rx { get; set; }
|
|
||||||
//public double ChargeLimitLow { get; set; }
|
|
||||||
//public double ChargeLimitHigh { get; set; }
|
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string AGV_PANID { get; set; }
|
public string AGV_PANID { get; set; }
|
||||||
@@ -328,7 +324,12 @@ namespace Project
|
|||||||
public Single interval_xbe { get; set; }
|
public Single interval_xbe { get; set; }
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public int interval_bms { get; set; }
|
public int interval_bms { get; set; }
|
||||||
public int doorSoundTerm { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// 스피커출력 간격
|
||||||
|
/// </summary>
|
||||||
|
[DisplayName("Speak Term")]
|
||||||
|
public int alarmSoundTerm { get; set; }
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public int musicvol { get; set; }
|
public int musicvol { get; set; }
|
||||||
@@ -336,6 +337,16 @@ namespace Project
|
|||||||
public bool Enable_Music { get; set; }
|
public bool Enable_Music { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region "Node Mapping"
|
||||||
|
|
||||||
|
[Category("Node Mapping"),DisplayName("Charger")]
|
||||||
|
public string NodeMAP_RFID_Charger { get; set; }
|
||||||
|
|
||||||
|
[Category("Node Mapping"), DisplayName("Home")]
|
||||||
|
public string NodeMAP_RFID_Home { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string LastMapFile { get; set; }
|
public string LastMapFile { get; set; }
|
||||||
|
|
||||||
@@ -366,6 +377,7 @@ namespace Project
|
|||||||
if (StatusInterval < 10) StatusInterval = 300; //5분간격
|
if (StatusInterval < 10) StatusInterval = 300; //5분간격
|
||||||
if (XBE_ID < 1) XBE_ID = 0xFF; //0은 acs 이므로 쓰지못하게한다.
|
if (XBE_ID < 1) XBE_ID = 0xFF; //0은 acs 이므로 쓰지못하게한다.
|
||||||
if (SAD == 0) SAD = 999;
|
if (SAD == 0) SAD = 999;
|
||||||
|
if (GDSValue == 0) GDSValue = 1000;
|
||||||
if (TAGF1A == 0)
|
if (TAGF1A == 0)
|
||||||
{
|
{
|
||||||
TAGNOT = 9000;
|
TAGNOT = 9000;
|
||||||
@@ -416,10 +428,10 @@ namespace Project
|
|||||||
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
|
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
|
||||||
if (interval_bms == 0) interval_bms = 10;
|
if (interval_bms == 0) interval_bms = 10;
|
||||||
|
|
||||||
//충전은 10분간격으로 재시도 한다
|
|
||||||
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
|
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
|
||||||
if (doorSoundTerm == 0) doorSoundTerm = 15; //기본 15초
|
if (alarmSoundTerm == 0) alarmSoundTerm = 15; //기본 15초
|
||||||
if (ChargeSearchTime == 0) ChargeSearchTime = 25;
|
if (ChargeSearchTime == 0) ChargeSearchTime = 25;
|
||||||
|
if (BatteryLimit_Low == 0) BatteryLimit_Low = 20;
|
||||||
//최대 충전진행 시간(기본 1시간)
|
//최대 충전진행 시간(기본 1시간)
|
||||||
if (ChargeMaxTime == 0) ChargeMaxTime = 3600;
|
if (ChargeMaxTime == 0) ChargeMaxTime = 3600;
|
||||||
// if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100;
|
// if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100;
|
||||||
|
|||||||
@@ -22,11 +22,31 @@ namespace Project
|
|||||||
public Color SMSG_ShadowColor = Color.Transparent;
|
public Color SMSG_ShadowColor = Color.Transparent;
|
||||||
public float SMSG_ProgressValue = 0;
|
public float SMSG_ProgressValue = 0;
|
||||||
public string SMSG_Tag = string.Empty;
|
public string SMSG_Tag = string.Empty;
|
||||||
//public event EventHandler SMSG_Update;
|
/// <summary>
|
||||||
//public void UpdateStatusMessage()
|
/// 이동대상위치(상차,하차,충전)
|
||||||
//{
|
/// </summary>
|
||||||
// SMSG_Update?.Invoke(null, null);
|
private ePosition _targetPos = ePosition.NONE;
|
||||||
//}
|
public string result_message = "";
|
||||||
|
public double result_progressmax = 0;
|
||||||
|
public double result_progressvalue = 0;
|
||||||
|
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
|
||||||
|
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
|
||||||
|
public string StopMessagePLC = string.Empty;
|
||||||
|
public string StopMessageSWR = string.Empty;
|
||||||
|
private ePosition _currentpos = ePosition.NONE;
|
||||||
|
private string _currentposcw = string.Empty;
|
||||||
|
public ushort LastTAG { get; set; } = 0;
|
||||||
|
public ePosition NextPos = ePosition.NONE;
|
||||||
|
private char _comandKit { get; set; }
|
||||||
|
public string Memo;
|
||||||
|
public eResult ResultCode { get; set; }
|
||||||
|
public eECode ResultErrorCode;
|
||||||
|
public string ResultMessage { get; set; }
|
||||||
|
public Boolean isError { get; set; }
|
||||||
|
public int retry = 0;
|
||||||
|
public DateTime retryTime;
|
||||||
|
public Device.Socket.Message RecvMessage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 작업시작시간
|
/// 작업시작시간
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -52,9 +72,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public DateTime ChargeStartTime = DateTime.Parse("1982-11-23");
|
|
||||||
|
|
||||||
|
|
||||||
#region "AGV Status Value"
|
#region "AGV Status Value"
|
||||||
public string PLC1_RawData { get; set; }
|
public string PLC1_RawData { get; set; }
|
||||||
public string PLC2_RawData { get; set; }
|
public string PLC2_RawData { get; set; }
|
||||||
@@ -62,26 +79,7 @@ namespace Project
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이동대상위치(상차,하차,충전)
|
|
||||||
/// </summary>
|
|
||||||
private ePosition _targetPos = ePosition.NONE;
|
|
||||||
|
|
||||||
public event EventHandler TargetPosSet;
|
|
||||||
|
|
||||||
public string result_message = "";
|
|
||||||
public double result_progressmax = 0;
|
|
||||||
public double result_progressvalue = 0;
|
|
||||||
|
|
||||||
|
|
||||||
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
|
|
||||||
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
|
|
||||||
public string StopMessagePLC = string.Empty;
|
|
||||||
public string StopMessageSWR = string.Empty;
|
|
||||||
//public DateTime LastChar
|
|
||||||
//geTime = DateTime.Parse("1982-11-23");
|
|
||||||
|
|
||||||
public ePosition NextPos = ePosition.NONE;
|
|
||||||
public ePosition TargetPos
|
public ePosition TargetPos
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -91,13 +89,11 @@ namespace Project
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
_targetPos = value;
|
_targetPos = value;
|
||||||
TargetPosSet?.Invoke(this, null);
|
|
||||||
|
|
||||||
PUB.log.Add(string.Format("대상위치설정:{0}", value));
|
PUB.log.Add(string.Format("대상위치설정:{0}", value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private char _comandKit { get; set; }
|
|
||||||
public char CommandKit
|
public char CommandKit
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -113,27 +109,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//private ePosition _currentPos = ePosition.NONE;
|
|
||||||
//public ePosition CurrentPos
|
|
||||||
//{
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// return _currentPos;
|
|
||||||
// }
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// if (_currentPos != value) //값이바뀔떄만 메세지 220628
|
|
||||||
// PUB.log.Add(string.Format("현재위치 설정:{0}->{1}", _currentPos, value));
|
|
||||||
|
|
||||||
// _currentPos = value;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
private ePosition _currentpos = ePosition.NONE;
|
|
||||||
private string _currentposcw = string.Empty;
|
|
||||||
|
|
||||||
// public ePosition LastPos = ePosition.NONE;
|
|
||||||
public string LastTAG { get; set; } = string.Empty;
|
|
||||||
public ePosition CurrentPos
|
public ePosition CurrentPos
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -164,11 +139,7 @@ namespace Project
|
|||||||
_currentposcw = value;
|
_currentposcw = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string Memo;
|
|
||||||
|
|
||||||
public eResult ResultCode { get; set; }
|
|
||||||
public eECode ResultErrorCode;
|
|
||||||
public string ResultMessage { get; set; }
|
|
||||||
|
|
||||||
#region "SetResultMessage"
|
#region "SetResultMessage"
|
||||||
|
|
||||||
@@ -207,13 +178,6 @@ namespace Project
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Boolean isError { get; set; }
|
|
||||||
|
|
||||||
public int retry = 0;
|
|
||||||
public DateTime retryTime;
|
|
||||||
public Device.Socket.Message RecvMessage;
|
|
||||||
|
|
||||||
public CResult()
|
public CResult()
|
||||||
{
|
{
|
||||||
this.Clear();
|
this.Clear();
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ namespace Project
|
|||||||
public static string AGV연결실패 { get { return string.Format("AGV {0}", 연결실패); } }
|
public static string AGV연결실패 { get { return string.Format("AGV {0}", 연결실패); } }
|
||||||
public static string 예측값이계산되지않아이동을중단합니다 = "이동 예측이 동작하지 않습니다. 개발부서에 문의 하세요";
|
public static string 예측값이계산되지않아이동을중단합니다 = "이동 예측이 동작하지 않습니다. 개발부서에 문의 하세요";
|
||||||
public static string 목적지이동이완료되었습니다 = "목적지 이동 이 완료 되었습니다";
|
public static string 목적지이동이완료되었습니다 = "목적지 이동 이 완료 되었습니다";
|
||||||
public static string 홈검색을시작합니다 = "홈검색을시작합니다";
|
public static string FindCurrentPisition = "현재 위치를 검색 합니다";
|
||||||
|
|
||||||
|
|
||||||
public static string 상차 = "상차";
|
public static string 상차 = "상차";
|
||||||
public static string 하차 = "하차";
|
public static string 하차 = "하차";
|
||||||
@@ -70,8 +69,8 @@ namespace Project
|
|||||||
public static string 커버를올립니다 = "커버를 올립니다";
|
public static string 커버를올립니다 = "커버를 올립니다";
|
||||||
|
|
||||||
public static string 홈위치로이동합니다 = "홈 위치로 이동 합니다";
|
public static string 홈위치로이동합니다 = "홈 위치로 이동 합니다";
|
||||||
public static string 하차작업이완료되었습니다 = "하차 작업이 완료 되었습니다";
|
public static string 버퍼도킹이완료되었습니다 = "버퍼 도킹이 완료 되었습니다";
|
||||||
public static string 상차작업이완료되었습니다 = "상차 작업이 완료 되었습니다";
|
public static string 버퍼도킹해제완료 = "버퍼 도킹이 해제 되었습니다";
|
||||||
public static string 커버업대기상태가아닙니다 = "커버 업 대기 상태가 아닙니다";
|
public static string 커버업대기상태가아닙니다 = "커버 업 대기 상태가 아닙니다";
|
||||||
public static string QC이동버튼은상하차에서만사용가능합니다 = "QC이동 버튼은 상,하차 에서만 사용 가능합니다";
|
public static string QC이동버튼은상하차에서만사용가능합니다 = "QC이동 버튼은 상,하차 에서만 사용 가능합니다";
|
||||||
public static string QA이동버튼은상하차에서만사용가능합니다 = "QA이동 버튼은 상,하차 에서만 사용 가능합니다";
|
public static string QA이동버튼은상하차에서만사용가능합니다 = "QA이동 버튼은 상,하차 에서만 사용 가능합니다";
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ using System.CodeDom;
|
|||||||
|
|
||||||
namespace arDev
|
namespace arDev
|
||||||
{
|
{
|
||||||
public class BMS : arRS232
|
public class BMS : BMSSerialComm
|
||||||
{
|
{
|
||||||
public BMS()
|
public BMS()
|
||||||
{
|
{
|
||||||
|
|
||||||
MinRecvLength = 34;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,28 +55,60 @@ namespace arDev
|
|||||||
{
|
{
|
||||||
tempBuffer.Add(incomByte);
|
tempBuffer.Add(incomByte);
|
||||||
|
|
||||||
var queylen = QueryIndex == 0 ? 34 : 23;
|
if (tempBuffer.Count > 7)
|
||||||
if (tempBuffer.Count == queylen)
|
|
||||||
{
|
{
|
||||||
if (incomByte != 0x77)
|
byte len = tempBuffer[3];
|
||||||
|
if (tempBuffer.Count >= 4 + len + 3) // Start+Reg+Status+Len + Data + Chk(2) + End
|
||||||
|
{
|
||||||
|
if (tempBuffer.Last() == 0x77)
|
||||||
|
{
|
||||||
|
//데이터가 맞게 수신됨
|
||||||
|
LastReceiveBuffer = tempBuffer.ToArray();
|
||||||
|
bComplete = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
//종단기호가 맞지 않다. 이자료는 폐기한다.
|
//종단기호가 맞지 않다. 이자료는 폐기한다.
|
||||||
var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
|
var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
|
||||||
RaiseMessage(MessageType.Error, $"discard : {hexstr}");
|
RaiseMessage(MessageType.Error, $"discard : {hexstr}");
|
||||||
tempBuffer.Clear();
|
tempBuffer.Clear();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
//데이터가 맞게 수신됨
|
|
||||||
LastReceiveBuffer = tempBuffer.ToArray();
|
|
||||||
bComplete = true;
|
|
||||||
}
|
|
||||||
findSTX = false;
|
findSTX = false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
//아직 모자르므로 대기한다
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [22 - 12 - 27 14:32:49] open: True
|
||||||
|
//[22 - 12 - 27 14:32:49] Send: DD A5 03 00 FF FD 77 0D
|
||||||
|
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
|
||||||
|
//[22 - 12 - 27 14:32:50] Recv: 26.61v,81.4 %
|
||||||
|
//[22 - 12 - 27 14:32:50] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
|
||||||
|
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
|
||||||
|
//[22 - 12 - 27 14:32:51] Recv: 26.61v,81.4 %
|
||||||
|
//[22 - 12 - 27 14:32:51] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
|
||||||
|
|
||||||
|
|
||||||
|
//var queylen = QueryIndex == 0 ? 34 : 23;
|
||||||
|
//if (tempBuffer.Count == queylen)
|
||||||
|
//{
|
||||||
|
// if (incomByte != 0x77)
|
||||||
|
// {
|
||||||
|
// //종단기호가 맞지 않다. 이자료는 폐기한다.
|
||||||
|
// var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
|
||||||
|
// RaiseMessage(MessageType.Error, $"discard : {hexstr}");
|
||||||
|
// tempBuffer.Clear();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// //데이터가 맞게 수신됨
|
||||||
|
// LastReceiveBuffer = tempBuffer.ToArray();
|
||||||
|
// bComplete = true;
|
||||||
|
// }
|
||||||
|
// findSTX = false;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// //아직 모자르므로 대기한다
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +138,7 @@ namespace arDev
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var rxstr = string.Join(" ", data.Select(t => t.ToString("X2")));
|
var rxstr = string.Join(" ", data.Select(t => t.ToString("X2")));
|
||||||
RaiseMessage(MessageType.Recv, $"Querh:{QueryIndex},Data:{rxstr}" );
|
RaiseMessage(MessageType.Recv, rxstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QueryIndex == 0)
|
if (QueryIndex == 0)
|
||||||
@@ -179,6 +211,13 @@ namespace arDev
|
|||||||
batH = (UInt16)(batH | batL);
|
batH = (UInt16)(batH | batL);
|
||||||
Current_Volt = (float)(batH / 100.0);
|
Current_Volt = (float)(batH / 100.0);
|
||||||
|
|
||||||
|
//충방전전류
|
||||||
|
Int16 batHi = (Int16)LastReceiveBuffer[6];
|
||||||
|
Int16 batLi = (Int16)LastReceiveBuffer[7];
|
||||||
|
batHi = (Int16)(batHi << 8);
|
||||||
|
batHi = (Int16)(batHi | batLi);
|
||||||
|
Charge_Amp = (float)(batHi / 100.0);
|
||||||
|
|
||||||
//잔량확인
|
//잔량확인
|
||||||
batH = (UInt16)LastReceiveBuffer[8];
|
batH = (UInt16)LastReceiveBuffer[8];
|
||||||
batL = (UInt16)LastReceiveBuffer[9];
|
batL = (UInt16)LastReceiveBuffer[9];
|
||||||
@@ -224,107 +263,80 @@ namespace arDev
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
private bool _autocharge = false;
|
/// 현재 충전중인지?
|
||||||
public Boolean AutoCharge
|
/// </summary>
|
||||||
{
|
public bool IsCharging { get; private set; }
|
||||||
get { return _autocharge; }
|
DateTime ChargeStart = DateTime.Now;
|
||||||
set { _autocharge = false; }
|
DateTime ChargeEnd = DateTime.Now;
|
||||||
}
|
|
||||||
|
|
||||||
//public void ClearManualChargeCheckValue()
|
|
||||||
//{
|
|
||||||
// chk_timee = new DateTime(1982, 11, 23);
|
|
||||||
// chk_times = new DateTime(1982, 11, 23);
|
|
||||||
// chk_valuee = 0f;
|
|
||||||
// chk_values = 0f;
|
|
||||||
//}
|
|
||||||
|
|
||||||
void CheckManualCharge()
|
void CheckManualCharge()
|
||||||
{
|
{
|
||||||
if (AutoCharge)
|
//충방전전력이 1보다 크면 충전으로 한다.
|
||||||
|
if (Charge_Amp > 0.1)
|
||||||
{
|
{
|
||||||
if (chk_timee.Year != 1982)
|
//기존에 충전상태가 OFF였다면 충전중으로 알려준다
|
||||||
|
if (IsCharging == false)
|
||||||
{
|
{
|
||||||
chk_timee = new DateTime(1982, 11, 23);
|
IsCharging = true;
|
||||||
chk_valuee = 999f;
|
ChargeStart = DateTime.Now;
|
||||||
}
|
ChargeEnd = new DateTime(1982, 11, 23);
|
||||||
if (chk_times.Year != 1982)
|
|
||||||
{
|
|
||||||
chk_times = new DateTime(1982, 11, 23);
|
|
||||||
chk_values = 999f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chk_times.Year == 1982)
|
|
||||||
{
|
|
||||||
chk_times = DateTime.Now;
|
|
||||||
chk_values = Current_Level;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (chk_timee.Year == 1982)
|
|
||||||
{
|
|
||||||
if ((Current_Level - chk_values) >= 0.1)
|
|
||||||
{
|
|
||||||
//충전중이다
|
|
||||||
chk_timee = DateTime.Now;
|
|
||||||
chk_valuee = Current_Level;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
|
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeStart, true, Current_Level));
|
||||||
}
|
|
||||||
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
|
|
||||||
|
|
||||||
}
|
|
||||||
else if ((Current_Level - chk_values) <= -0.1)
|
|
||||||
{
|
|
||||||
//방전중이다
|
|
||||||
if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
|
|
||||||
if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//아직 변화가 없으니 종료일을 기록하지 않는다
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//이미 종료일이 셋팅된 상태이다
|
|
||||||
if ((Current_Level - chk_valuee) >= 0.1)
|
|
||||||
{
|
|
||||||
//종료시간을 시작값에 넣는다
|
|
||||||
chk_times = chk_timee;
|
|
||||||
chk_values = chk_valuee;
|
|
||||||
|
|
||||||
chk_timee = DateTime.Now;
|
|
||||||
chk_valuee = Current_Level;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
|
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
|
||||||
}
|
}
|
||||||
else if ((Current_Level - chk_valuee) <= -0.1)
|
else
|
||||||
{
|
{
|
||||||
//방전중이다
|
//충전상태가 유지되고 있다.
|
||||||
if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
|
}
|
||||||
if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//아직 변화가 없으니 종료일을 기록하지 않는다
|
//충전이해제되었다.. 단 바로 해제하지않고 1초정도 텀을 주고 OFF한다.
|
||||||
|
if (IsCharging)
|
||||||
|
{
|
||||||
|
if (ChargeEnd.Year == 1982)
|
||||||
|
{
|
||||||
|
ChargeEnd = DateTime.Now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var ts = DateTime.Now - ChargeEnd;
|
||||||
|
if (ts.TotalSeconds > 2) //충전종료시그널후 2초후에 충전off를 알린다.
|
||||||
|
{
|
||||||
|
ChargeEnd = DateTime.Now;
|
||||||
|
IsCharging = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeEnd, false, Current_Level));
|
||||||
|
}
|
||||||
|
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//방전상태가 유지되고 있다.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
|
public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
|
||||||
public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
|
public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
|
||||||
public float chk_values { get; set; } = 0f;
|
public float chk_values { get; set; } = 0f;
|
||||||
public float chk_valuee { get; set; } = 0f;
|
public float chk_valuee { get; set; } = 0f;
|
||||||
|
|
||||||
|
public float Charge_Amp { get; set; } = 0f;
|
||||||
|
public Int16 Charge_watt
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (Int16)((Charge_Amp) * Current_Volt);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 전압
|
/// 전압
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -407,7 +419,8 @@ namespace arDev
|
|||||||
cmd.Add(0xFF);
|
cmd.Add(0xFF);
|
||||||
cmd.Add(0xFD);
|
cmd.Add(0xFD);
|
||||||
cmd.Add(0x77);
|
cmd.Add(0x77);
|
||||||
cmd.Add(0x0D);
|
//cmd.Add(0x0D);
|
||||||
|
//_device.DiscardInBuffer();
|
||||||
return WriteData(cmd.ToArray());
|
return WriteData(cmd.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +436,8 @@ namespace arDev
|
|||||||
cmd.Add(0xFF);
|
cmd.Add(0xFF);
|
||||||
cmd.Add(0xFC);
|
cmd.Add(0xFC);
|
||||||
cmd.Add(0x77);
|
cmd.Add(0x77);
|
||||||
cmd.Add(0x0D);
|
//cmd.Add(0x0D);
|
||||||
|
//_device.DiscardInBuffer();
|
||||||
return WriteData(cmd.ToArray());
|
return WriteData(cmd.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ namespace arDev
|
|||||||
{
|
{
|
||||||
public class ChargetDetectArgs : EventArgs
|
public class ChargetDetectArgs : EventArgs
|
||||||
{
|
{
|
||||||
public DateTime times { get; set; }
|
public DateTime time { get; set; }
|
||||||
public DateTime timee { get; set; }
|
public float level { get; set; }
|
||||||
public float values { get; set; }
|
public bool Detected { get; set; }
|
||||||
public float valuee { get; set; }
|
public ChargetDetectArgs(DateTime times, bool detected, float values)
|
||||||
public ChargetDetectArgs(DateTime times, float values, DateTime timee, float valuee)
|
|
||||||
{
|
{
|
||||||
this.times = times;
|
this.time = times;
|
||||||
this.times = timee;
|
this.level = values;
|
||||||
this.values = values;
|
this.Detected = detected;
|
||||||
this.valuee = valuee;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class BMSInformationEventArgs : EventArgs
|
public class BMSInformationEventArgs : EventArgs
|
||||||
|
|||||||
607
Cs_HMI/Project/Device/BMSSerialComm.cs
Normal file
607
Cs_HMI/Project/Device/BMSSerialComm.cs
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace arDev
|
||||||
|
{
|
||||||
|
public abstract class BMSSerialComm : ISerialComm, IDisposable
|
||||||
|
{
|
||||||
|
protected System.IO.Ports.SerialPort _device;
|
||||||
|
protected ManualResetEvent _mre;
|
||||||
|
protected byte[] LastReceiveBuffer = new byte[] { };
|
||||||
|
/// <summary>
|
||||||
|
/// 최종 전송 메세지
|
||||||
|
/// </summary>
|
||||||
|
public byte[] lastSendBuffer = new byte[] { };
|
||||||
|
//public int ValidCheckTimeMSec { get; set; } = 5000;
|
||||||
|
protected List<byte> tempBuffer = new List<byte>();
|
||||||
|
protected Boolean findSTX = false;
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
public DateTime LastConnTime { get; set; }
|
||||||
|
public DateTime LastConnTryTime { get; set; }
|
||||||
|
public DateTime lastSendTime;
|
||||||
|
/// <summary>
|
||||||
|
/// 메세지 수신시 사용하는 내부버퍼
|
||||||
|
/// </summary>
|
||||||
|
protected List<byte> _buffer = new List<byte>();
|
||||||
|
/// <summary>
|
||||||
|
/// 데이터조회간격(초)
|
||||||
|
/// </summary>
|
||||||
|
public float ScanInterval { get; set; }
|
||||||
|
|
||||||
|
// public byte[] LastRecvData;
|
||||||
|
public string LastRecvString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LastReceiveBuffer == null) return String.Empty;
|
||||||
|
else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 마지막으로 데이터를 받은 시간
|
||||||
|
/// </summary>
|
||||||
|
public DateTime lastRecvTime;
|
||||||
|
|
||||||
|
|
||||||
|
public int WriteError = 0;
|
||||||
|
public string WriteErrorMessage = string.Empty;
|
||||||
|
public int WaitTimeout { get; set; } = 1000;
|
||||||
|
public int MinRecvLength { get; set; } = 1;
|
||||||
|
|
||||||
|
// Polling Thread related
|
||||||
|
protected Thread _recvThread;
|
||||||
|
protected volatile bool _isReading = false;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 포트이름
|
||||||
|
/// </summary>
|
||||||
|
[Description("시리얼 포트 이름")]
|
||||||
|
[Category("설정"), DisplayName("Port Name")]
|
||||||
|
public string PortName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return string.Empty;
|
||||||
|
else return _device.PortName;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsOpen)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
|
||||||
|
}
|
||||||
|
else if (String.IsNullOrEmpty(value) == false)
|
||||||
|
_device.PortName = value;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BaudRate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return 0;
|
||||||
|
else return _device.BaudRate;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsOpen)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
|
||||||
|
}
|
||||||
|
else if (value != 0)
|
||||||
|
_device.BaudRate = value;
|
||||||
|
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BMSSerialComm()
|
||||||
|
{
|
||||||
|
_device = new System.IO.Ports.SerialPort();
|
||||||
|
this.BaudRate = 9600;
|
||||||
|
ScanInterval = 10;
|
||||||
|
// _device.DataReceived += barcode_DataReceived; // Removed event handler
|
||||||
|
_device.ErrorReceived += this.barcode_ErrorReceived;
|
||||||
|
_device.WriteTimeout = 3000;
|
||||||
|
_device.ReadTimeout = 3000;
|
||||||
|
_device.ReadBufferSize = 8192;
|
||||||
|
_device.WriteBufferSize = 8192;
|
||||||
|
//_device.DiscardInBuffer();
|
||||||
|
//_device.DiscardOutBuffer();
|
||||||
|
ErrorMessage = string.Empty;
|
||||||
|
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||||
|
LastConnTime = DateTime.Parse("1982-11-23");
|
||||||
|
LastConnTryTime = DateTime.Parse("1982-11-23");
|
||||||
|
lastRecvTime = DateTime.Parse("1982-11-23");
|
||||||
|
this._mre = new ManualResetEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
~BMSSerialComm()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Flag: Has Dispose already been called?
|
||||||
|
bool disposed = false;
|
||||||
|
|
||||||
|
// Public implementation of Dispose pattern callable by consumers.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected implementation of Dispose pattern.
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Free any other managed objects here.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop reading thread
|
||||||
|
_isReading = false;
|
||||||
|
|
||||||
|
// _device.DataReceived -= barcode_DataReceived; // Removed event handler
|
||||||
|
_device.ErrorReceived -= this.barcode_ErrorReceived;
|
||||||
|
|
||||||
|
if (_recvThread != null && _recvThread.IsAlive)
|
||||||
|
{
|
||||||
|
_recvThread.Join(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_device != null)
|
||||||
|
{
|
||||||
|
if (_device.IsOpen) _device.Close();
|
||||||
|
_device.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free any unmanaged objects here.
|
||||||
|
//
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean Open()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_device.IsOpen == false)
|
||||||
|
{
|
||||||
|
_device.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_device.IsOpen)
|
||||||
|
{
|
||||||
|
// Start polling thread
|
||||||
|
if (_isReading == false)
|
||||||
|
{
|
||||||
|
_isReading = true;
|
||||||
|
_recvThread = new Thread(ReadPort);
|
||||||
|
_recvThread.IsBackground = true;
|
||||||
|
_recvThread.Start();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string GetHexString(Byte[] input)
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
|
foreach (byte b in input)
|
||||||
|
sb.Append(" " + b.ToString("X2"));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 포트가 열려있는지 확인
|
||||||
|
/// </summary>
|
||||||
|
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
|
||||||
|
[Category("정보"), DisplayName("Port Open")]
|
||||||
|
public Boolean IsOpen
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_device == null) return false;
|
||||||
|
return _device.IsOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Close()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isReading = false; // Stop thread loop
|
||||||
|
|
||||||
|
if (_recvThread != null && _recvThread.IsAlive)
|
||||||
|
{
|
||||||
|
if (!_recvThread.Join(500)) // Wait for thread to finish
|
||||||
|
{
|
||||||
|
// _recvThread.Abort(); // Avoid Abort if possible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_device != null && _device.IsOpen)
|
||||||
|
{
|
||||||
|
_device.DiscardInBuffer();
|
||||||
|
_device.DiscardOutBuffer();
|
||||||
|
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected Boolean RaiseRecvData()
|
||||||
|
{
|
||||||
|
return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 수신받은 메세지를 발생 시킵니다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
|
||||||
|
{
|
||||||
|
//181206 - 최종수신 메세지 기록
|
||||||
|
lastRecvTime = DateTime.Now;
|
||||||
|
if (udpatelastbuffer && Data != null)
|
||||||
|
{
|
||||||
|
if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
|
||||||
|
{
|
||||||
|
LastReceiveBuffer = new byte[Data.Length];
|
||||||
|
Array.Copy(Data, LastReceiveBuffer, Data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// UI update might need Invoke if this event handler updates UI directly,
|
||||||
|
// but usually the subscriber handles Invoke.
|
||||||
|
// Since we are running on a background thread now, subscribers must be aware.
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||||
|
if (ProcessRecvData(Data) == false)
|
||||||
|
{
|
||||||
|
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true)); //errormessage
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.ErrorMessage = ex.Message;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 수신받은 자료를 처리한다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract bool ProcessRecvData(byte[] data);
|
||||||
|
|
||||||
|
#region "Internal Events"
|
||||||
|
|
||||||
|
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = new byte[] { };
|
||||||
|
|
||||||
|
// Replaced with ReadPort Loop
|
||||||
|
/*
|
||||||
|
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int ReadCount = _device.BytesToRead;
|
||||||
|
|
||||||
|
buffer = new byte[ReadCount];
|
||||||
|
_device.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
System.Text.StringBuilder LogMsg = new StringBuilder();
|
||||||
|
|
||||||
|
byte[] remainBuffer;
|
||||||
|
Repeat:
|
||||||
|
if (CustomParser(buffer, out remainBuffer))
|
||||||
|
{
|
||||||
|
//분석완료이므로 받은 데이터를 버퍼에 기록한다
|
||||||
|
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
|
||||||
|
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
|
||||||
|
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
|
||||||
|
tempBuffer.Clear();
|
||||||
|
|
||||||
|
//수신메세지발생
|
||||||
|
RaiseRecvData();
|
||||||
|
if (remainBuffer != null && remainBuffer.Length > 0)
|
||||||
|
{
|
||||||
|
//버퍼를 변경해서 다시 전송을 해준다.
|
||||||
|
Array.Resize(ref buffer, remainBuffer.Length);
|
||||||
|
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
|
||||||
|
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//if (IsOpen)
|
||||||
|
//{
|
||||||
|
// //_device.DiscardInBuffer();
|
||||||
|
// //_device.DiscardOutBuffer();
|
||||||
|
//}
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ReadPort()
|
||||||
|
{
|
||||||
|
while (_isReading)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_device == null || !_device.IsOpen)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readCount = _device.BytesToRead;
|
||||||
|
if (readCount > 0)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[readCount];
|
||||||
|
_device.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
byte[] remainBuffer;
|
||||||
|
Repeat:
|
||||||
|
if (CustomParser(buffer, out remainBuffer))
|
||||||
|
{
|
||||||
|
//분석완료이므로 받은 데이터를 버퍼에 기록한다
|
||||||
|
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
|
||||||
|
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
|
||||||
|
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
|
||||||
|
tempBuffer.Clear();
|
||||||
|
|
||||||
|
//수신메세지발생
|
||||||
|
RaiseRecvData();
|
||||||
|
if (remainBuffer != null && remainBuffer.Length > 0)
|
||||||
|
{
|
||||||
|
//버퍼를 변경해서 다시 전송을 해준다.
|
||||||
|
buffer = new byte[remainBuffer.Length]; // Reallocate buffer for remaining data
|
||||||
|
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
|
||||||
|
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(20); // Data 없음, 대기
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Thread 상에서 Exception 발생 시 로그 남기고 계속 진행 여부 결정
|
||||||
|
// 여기서는 에러 메시지 발생시키고 Sleep
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "External Events"
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 오류 및 기타 일반 메세지
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<MessageEventArgs> Message;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "Event Args"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 데이터를 수신할떄 사용함(RAW 포함)
|
||||||
|
/// </summary>
|
||||||
|
public class ReceiveDataEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private byte[] _buffer = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 바이트배열의 버퍼값
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Value { get { return _buffer; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
|
||||||
|
/// </summary>
|
||||||
|
public string StrValue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
//return string.Empty;
|
||||||
|
|
||||||
|
if (_buffer == null || _buffer.Length < 1) return string.Empty;
|
||||||
|
else return System.Text.Encoding.Default.GetString(_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public ReceiveDataEventArgs(byte[] buffer)
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메세지를 강제 발생
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mt"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
protected virtual void RaiseMessage(MessageType mt, string message)
|
||||||
|
{
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
|
||||||
|
}
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Error,
|
||||||
|
Send,
|
||||||
|
Recv,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MessageEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MessageType MsgType { get; set; }
|
||||||
|
private string _message = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recv,Send,Normal,Error 모두 지원
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get { return _message; } }
|
||||||
|
|
||||||
|
private byte[] _data = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recv,Send에서만 값이 존재 합니다
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Data { get { return _data; } }
|
||||||
|
public MessageEventArgs(string Message, bool isError = false)
|
||||||
|
{
|
||||||
|
if (isError) MsgType = MessageType.Error;
|
||||||
|
else MsgType = MessageType.Normal;
|
||||||
|
_message = Message;
|
||||||
|
}
|
||||||
|
public MessageEventArgs(MessageType msgtype, string Message)
|
||||||
|
{
|
||||||
|
MsgType = msgtype;
|
||||||
|
_message = Message;
|
||||||
|
_data = System.Text.Encoding.Default.GetBytes(Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageEventArgs(byte[] buffer, bool isRecv = true)
|
||||||
|
{
|
||||||
|
if (isRecv) MsgType = MessageType.Recv;
|
||||||
|
else MsgType = MessageType.Send;
|
||||||
|
_data = new byte[buffer.Length];
|
||||||
|
Array.Copy(buffer, _data, Data.Length);
|
||||||
|
_message = System.Text.Encoding.Default.GetString(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
|
||||||
|
/// </summary>
|
||||||
|
public Boolean IsValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsOpen == false) return false;
|
||||||
|
if (lastRecvTime.Year == 1982) return false;
|
||||||
|
var ts = DateTime.Now - lastRecvTime;
|
||||||
|
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected bool WriteData(string cmd)
|
||||||
|
{
|
||||||
|
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
|
||||||
|
/// </summary>
|
||||||
|
protected Boolean WriteData(byte[] data)
|
||||||
|
{
|
||||||
|
Boolean bRet = false;
|
||||||
|
|
||||||
|
//171205 : 타임아웃시간추가
|
||||||
|
if (!_mre.WaitOne(WaitTimeout))
|
||||||
|
{
|
||||||
|
ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mre.Reset();
|
||||||
|
|
||||||
|
//Array.Resize(ref data, data.Length + 2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lastSendTime = DateTime.Now;
|
||||||
|
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
|
||||||
|
else Array.Resize(ref lastSendBuffer, data.Length);
|
||||||
|
Array.Copy(data, lastSendBuffer, data.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
_device.Write(data, i, 1);
|
||||||
|
|
||||||
|
//_device.Write(data, 0, data.Length);
|
||||||
|
|
||||||
|
//171113
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(data, false));
|
||||||
|
|
||||||
|
bRet = true;
|
||||||
|
WriteError = 0;
|
||||||
|
WriteErrorMessage = string.Empty;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// this.isinit = false;
|
||||||
|
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||||
|
bRet = false;
|
||||||
|
WriteError += 1; //연속쓰기오류횟수
|
||||||
|
WriteErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mre.Set();
|
||||||
|
}
|
||||||
|
return bRet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,11 +14,11 @@ using System.Windows.Forms;
|
|||||||
|
|
||||||
namespace Project.Device
|
namespace Project.Device
|
||||||
{
|
{
|
||||||
public class Xbee : SerialPort
|
public class Xbee : SerialPort, arDev.ISerialComm
|
||||||
{
|
{
|
||||||
public string buffer = string.Empty;
|
public string buffer = string.Empty;
|
||||||
public System.Text.StringBuilder newbuffer = new StringBuilder();
|
public System.Text.StringBuilder newbuffer = new StringBuilder();
|
||||||
public string errorMessage = string.Empty;
|
public string ErrorMessage { get; set; } = string.Empty;
|
||||||
public DateTime LastStatusSendTime { get; set; } = DateTime.Now;
|
public DateTime LastStatusSendTime { get; set; } = DateTime.Now;
|
||||||
private EEProtocol proto;
|
private EEProtocol proto;
|
||||||
|
|
||||||
@@ -38,6 +38,8 @@ namespace Project.Device
|
|||||||
|
|
||||||
public Xbee()
|
public Xbee()
|
||||||
{
|
{
|
||||||
|
this.WriteTimeout = 500;
|
||||||
|
this.ReadTimeout = 500;
|
||||||
this.DataReceived += Xbee_DataReceived;
|
this.DataReceived += Xbee_DataReceived;
|
||||||
proto = new EEProtocol();
|
proto = new EEProtocol();
|
||||||
proto.OnDataReceived += Proto_OnDataReceived;
|
proto.OnDataReceived += Proto_OnDataReceived;
|
||||||
@@ -65,11 +67,23 @@ namespace Project.Device
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = ex.Message;
|
ErrorMessage = ex.Message;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public new bool Close()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
base.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public new bool Open()
|
public new bool Open()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -79,8 +93,8 @@ namespace Project.Device
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = ex.Message;
|
ErrorMessage = ex.Message;
|
||||||
PUB.logxbee.AddE(errorMessage);
|
PUB.logxbee.AddE(ErrorMessage);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,19 +106,14 @@ namespace Project.Device
|
|||||||
var cmd = e.ReceivedPacket.Command.ToString("X2");
|
var cmd = e.ReceivedPacket.Command.ToString("X2");
|
||||||
var id = e.ReceivedPacket.ID.ToString("X2");
|
var id = e.ReceivedPacket.ID.ToString("X2");
|
||||||
PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
|
PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
|
||||||
|
|
||||||
ProtocReceived?.Invoke(this, e);
|
ProtocReceived?.Invoke(this, e);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
|
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
|
||||||
{
|
{
|
||||||
MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message));
|
MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||||
{
|
{
|
||||||
var dev = sender as System.IO.Ports.SerialPort;
|
var dev = sender as System.IO.Ports.SerialPort;
|
||||||
@@ -112,6 +121,7 @@ namespace Project.Device
|
|||||||
dev.Read(buffer, 0, buffer.Length);
|
dev.Read(buffer, 0, buffer.Length);
|
||||||
proto.ProcessReceivedData(buffer);
|
proto.ProcessReceivedData(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 이동완료 신호 전송
|
/// 이동완료 신호 전송
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -147,13 +157,28 @@ namespace Project.Device
|
|||||||
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
|
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
|
||||||
if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);
|
if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);
|
||||||
|
|
||||||
var data = new byte[] { (byte)errcode };
|
var data = new List<byte>();
|
||||||
|
data.Add((byte)errcode);
|
||||||
var datamsg = System.Text.Encoding.Default.GetBytes(errormessage);
|
var datamsg = System.Text.Encoding.Default.GetBytes(errormessage);
|
||||||
|
data.AddRange(datamsg);
|
||||||
|
|
||||||
var packet = proto.CreatePacket(id, cmd, data);
|
var packet = proto.CreatePacket(id, cmd, data.ToArray());
|
||||||
Send(packet);
|
Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool BufferInReady { get; set; }
|
||||||
|
public bool BufferInComplete { get; set; }
|
||||||
|
public bool BufferOutComplete { get; set; }
|
||||||
|
public bool BufferReadyError { get; set; }
|
||||||
|
|
||||||
|
public bool LoaderInComplete { get; set; }
|
||||||
|
public bool LoaderOutComplete { get; set; }
|
||||||
|
public bool UnloaderInComplete { get; set; }
|
||||||
|
public bool UnloaderOutComplete { get; set; }
|
||||||
|
public bool CleanerInComplete { get; set; }
|
||||||
|
public bool CleanerOutComplete { get; set; }
|
||||||
|
|
||||||
|
ManualResetEvent sendlock = new ManualResetEvent(true);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -161,6 +186,10 @@ namespace Project.Device
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SendStatus()
|
public void SendStatus()
|
||||||
{
|
{
|
||||||
|
if (this.IsOpen == false) return;
|
||||||
|
if (sendlock.WaitOne() == false) return;
|
||||||
|
sendlock.Reset();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mode[1] : 0=manual, 1=auto
|
Mode[1] : 0=manual, 1=auto
|
||||||
RunSt[1] : 0=stop, 1=run, 2=error
|
RunSt[1] : 0=stop, 1=run, 2=error
|
||||||
@@ -211,17 +240,17 @@ namespace Project.Device
|
|||||||
data[5] = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
|
data[5] = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
|
||||||
|
|
||||||
// CartSt
|
// CartSt
|
||||||
if (PUB.AGV.signal.cart_detect1 && PUB.AGV.signal.cart_detect2)
|
if (PUB.AGV.signal2.cart_detect1 && PUB.AGV.signal2.cart_detect2)
|
||||||
data[6] = 1; // 센서두개가 모두 감지되는 경우
|
data[6] = 1; // 센서두개가 모두 감지되는 경우
|
||||||
else if (PUB.AGV.signal.cart_detect1 == false && PUB.AGV.signal.cart_detect2 == false)
|
else if (PUB.AGV.signal2.cart_detect1 == false && PUB.AGV.signal2.cart_detect2 == false)
|
||||||
data[6] = 0; // 센서두개가 모두 감지되지 않는 경우
|
data[6] = 0; // 센서두개가 모두 감지되지 않는 경우
|
||||||
else
|
else
|
||||||
data[6] = 2; // 센서하나만 감지되는 경우
|
data[6] = 2; // 센서하나만 감지되는 경우
|
||||||
|
|
||||||
// LiftSt
|
// LiftSt
|
||||||
if (PUB.AGV.signal.lift_up)
|
if (PUB.AGV.signal1.lift_up)
|
||||||
data[7] = 1; // 위로 올라가는 경우
|
data[7] = 1; // 위로 올라가는 경우
|
||||||
else if (PUB.AGV.signal.lift_down)
|
else if (PUB.AGV.signal1.lift_down)
|
||||||
data[7] = 0; // 아래로 내려가는 경우
|
data[7] = 0; // 아래로 내려가는 경우
|
||||||
else
|
else
|
||||||
data[7] = 2; // unknown (기본값)
|
data[7] = 2; // unknown (기본값)
|
||||||
@@ -235,14 +264,21 @@ namespace Project.Device
|
|||||||
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
|
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
|
||||||
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data);
|
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data);
|
||||||
if (Send(packet))
|
if (Send(packet))
|
||||||
PUB.logxbee.AddI($"Send status {packet.Length} {packet.HexString()}");
|
PUB.logxbee.AddI($"Send status [O] : {packet.Length} {packet.HexString()}");
|
||||||
|
else
|
||||||
|
PUB.logxbee.AddE($"Send status [X] : {packet.Length} {packet.HexString()}");
|
||||||
LastStatusSendTime = DateTime.Now;
|
LastStatusSendTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = ex.Message;
|
ErrorMessage = ex.Message;
|
||||||
PUB.logxbee.AddE(errorMessage);
|
PUB.logxbee.AddE(ErrorMessage);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sendlock.Set();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,311 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using AR;
|
|
||||||
using arCtl;
|
|
||||||
using COMM;
|
|
||||||
using Project.StateMachine;
|
|
||||||
|
|
||||||
namespace Project
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 연결 및 상태 전송을 담당하는 별도 태스크
|
|
||||||
/// </summary>
|
|
||||||
public partial class fMain
|
|
||||||
{
|
|
||||||
// 장치 관리 태스크 관련
|
|
||||||
private Task deviceManagementTask;
|
|
||||||
private CancellationTokenSource deviceManagementCts;
|
|
||||||
private volatile bool isDeviceManagementRunning = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 태스크 시작 (IDLE 상태 진입 시 호출)
|
|
||||||
/// </summary>
|
|
||||||
public void StartDeviceManagementTask()
|
|
||||||
{
|
|
||||||
if (isDeviceManagementRunning)
|
|
||||||
{
|
|
||||||
PUB.log.Add("DeviceManagement", "이미 실행 중입니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDeviceManagementRunning = true;
|
|
||||||
deviceManagementCts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
deviceManagementTask = Task.Factory.StartNew(
|
|
||||||
() => DeviceManagementWorker(deviceManagementCts.Token),
|
|
||||||
deviceManagementCts.Token,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default
|
|
||||||
);
|
|
||||||
|
|
||||||
PUB.log.Add("DeviceManagement", "장치 관리 태스크 시작");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 태스크 종료
|
|
||||||
/// File : /device/_DeviceManagement.cs
|
|
||||||
/// </summary>
|
|
||||||
public void StopDeviceManagementTask()
|
|
||||||
{
|
|
||||||
if (!isDeviceManagementRunning)
|
|
||||||
return;
|
|
||||||
|
|
||||||
isDeviceManagementRunning = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
deviceManagementCts?.Cancel();
|
|
||||||
|
|
||||||
if (deviceManagementTask != null)
|
|
||||||
{
|
|
||||||
if (!deviceManagementTask.Wait(3000)) // 3초 대기
|
|
||||||
{
|
|
||||||
PUB.log.AddE("DeviceManagement:태스크 종료 대기 시간 초과");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"DeviceManagement 종료 중 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
deviceManagementCts?.Dispose();
|
|
||||||
deviceManagementCts = null;
|
|
||||||
deviceManagementTask = null;
|
|
||||||
PUB.log.Add("DeviceManagement", "장치 관리 태스크 종료");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 워커 (별도 태스크에서 실행)
|
|
||||||
/// - 장치 연결 관리 (AGV, XBee, BMS)
|
|
||||||
/// - 자동 상태 전송 (XBee, BMS)
|
|
||||||
/// </summary>
|
|
||||||
private void DeviceManagementWorker(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
PUB.log.Add("DeviceManagementWorker", "시작");
|
|
||||||
|
|
||||||
DateTime lastXbeStatusSendTime = DateTime.Now;
|
|
||||||
DateTime lastBmsQueryTime = DateTime.Now;
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested && isDeviceManagementRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 상태머신이 IDLE 이상이고 CLOSING 미만일 때만 동작
|
|
||||||
if (PUB.sm.Step >= eSMStep.IDLE && PUB.sm.Step < eSMStep.CLOSING)
|
|
||||||
{
|
|
||||||
// ========== 1. 장치 연결 관리 ==========
|
|
||||||
ManageDeviceConnections();
|
|
||||||
|
|
||||||
// ========== 2. XBee 상태 전송 ==========
|
|
||||||
if (PUB.XBE != null && PUB.XBE.IsOpen)
|
|
||||||
{
|
|
||||||
var tsXbe = DateTime.Now - lastXbeStatusSendTime;
|
|
||||||
if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe)
|
|
||||||
{
|
|
||||||
lastXbeStatusSendTime = DateTime.Now;
|
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PUB.XBE.SendStatus();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 3. BMS 쿼리 및 배터리 경고 ==========
|
|
||||||
if (PUB.BMS != null && PUB.BMS.IsOpen)
|
|
||||||
{
|
|
||||||
var tsBms = DateTime.Now - lastBmsQueryTime;
|
|
||||||
if (tsBms.TotalSeconds >= PUB.setting.interval_bms)
|
|
||||||
{
|
|
||||||
lastBmsQueryTime = DateTime.Now;
|
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PUB.BMS.SendQuery();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 배터리 경고음
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Update_BatteryWarnSpeak();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"DeviceManagementWorker 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1초 대기 (또는 취소 요청 시 즉시 종료)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Task.Delay(1000, cancellationToken).Wait();
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PUB.log.Add("DeviceManagementWorker", "종료");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 연결 상태 관리
|
|
||||||
/// </summary>
|
|
||||||
private void ManageDeviceConnections()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// AGV 연결
|
|
||||||
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
|
|
||||||
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
|
|
||||||
|
|
||||||
// XBee 연결
|
|
||||||
ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE,
|
|
||||||
eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE);
|
|
||||||
|
|
||||||
// BMS 연결
|
|
||||||
if (PUB.BMS.IsOpen == false)
|
|
||||||
{
|
|
||||||
var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT);
|
|
||||||
if (ts.TotalSeconds > 3)
|
|
||||||
{
|
|
||||||
PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}");
|
|
||||||
PUB.BMS.PortName = PUB.setting.Port_BAT;
|
|
||||||
if (PUB.BMS.Open())
|
|
||||||
PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})");
|
|
||||||
|
|
||||||
VAR.TIME.Update(eVarTime.LastConn_BAT);
|
|
||||||
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (PUB.BMS.IsValid == false)
|
|
||||||
{
|
|
||||||
var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT);
|
|
||||||
if (ts.TotalSeconds > (PUB.setting.interval_bms * 2.5))
|
|
||||||
{
|
|
||||||
PUB.log.Add("BMS 자동 연결 해제 (응답 없음)");
|
|
||||||
PUB.BMS.Close();
|
|
||||||
VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"ManageDeviceConnections 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 시리얼 포트 연결 (arDev.arRS232)
|
|
||||||
/// </summary>
|
|
||||||
void ConnectSerialPort(arDev.arRS232 dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
|
||||||
{
|
|
||||||
if (dev.IsOpen == false && port.isEmpty() == false)
|
|
||||||
{
|
|
||||||
var tsPLC = VAR.TIME.RUN(conntry);
|
|
||||||
if (tsPLC.TotalSeconds > 5)
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(recvtime);
|
|
||||||
dev.PortName = port;
|
|
||||||
dev.BaudRate = baud;
|
|
||||||
if (dev.Open())
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"[AGV:{port}:{baud}] 연결 완료");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var errmessage = dev.errorMessage;
|
|
||||||
PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}");
|
|
||||||
}
|
|
||||||
VAR.TIME.Update(conn);
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dev.PortName.Equals(port) == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
|
||||||
dev.Close();
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 시리얼 포트 연결 (Device.Xbee)
|
|
||||||
/// </summary>
|
|
||||||
void ConnectSerialPort(Device.Xbee dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
|
||||||
{
|
|
||||||
if (dev.IsOpen == false && port.isEmpty() == false)
|
|
||||||
{
|
|
||||||
var tsPLC = VAR.TIME.RUN(conntry);
|
|
||||||
if (tsPLC.TotalSeconds > 5)
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(recvtime);
|
|
||||||
dev.PortName = port;
|
|
||||||
dev.BaudRate = baud;
|
|
||||||
if (dev.Open())
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"[XBEE:{port}:{baud}] 연결 완료");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var errmessage = dev.errorMessage;
|
|
||||||
PUB.log.AddE($"[XBEE:{port}:{baud}] {errmessage}");
|
|
||||||
}
|
|
||||||
VAR.TIME.Update(conn);
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dev.PortName.Equals(port) == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
|
||||||
dev.Close();
|
|
||||||
VAR.TIME[(int)conntry] = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,815 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms; // required for Message
|
|
||||||
using System.Runtime.InteropServices; // required for Marshal
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.Win32.SafeHandles;
|
|
||||||
// DriveDetector - rev. 1, Oct. 31 2007
|
|
||||||
|
|
||||||
namespace usbdetect
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Hidden Form which we use to receive Windows messages about flash drives
|
|
||||||
/// </summary>
|
|
||||||
internal class DetectorForm : Form
|
|
||||||
{
|
|
||||||
private Label label1;
|
|
||||||
private DriveDetector mDetector = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set up the hidden form.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="detector">DriveDetector object which will receive notification about USB drives, see WndProc</param>
|
|
||||||
public DetectorForm(DriveDetector detector)
|
|
||||||
{
|
|
||||||
mDetector = detector;
|
|
||||||
this.MinimizeBox = false;
|
|
||||||
this.MaximizeBox = false;
|
|
||||||
this.ShowInTaskbar = false;
|
|
||||||
this.ShowIcon = false;
|
|
||||||
this.FormBorderStyle = FormBorderStyle.None;
|
|
||||||
this.Load += new System.EventHandler(this.Load_Form);
|
|
||||||
this.Activated += new EventHandler(this.Form_Activated);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Load_Form(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// We don't really need this, just to display the label in designer ...
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
// Create really small form, invisible anyway.
|
|
||||||
this.Size = new System.Drawing.Size(5, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Form_Activated(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
this.Visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This function receives all the windows messages for this window (form).
|
|
||||||
/// We call the DriveDetector from here so that is can pick up the messages about
|
|
||||||
/// drives arrived and removed.
|
|
||||||
/// </summary>
|
|
||||||
protected override void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
base.WndProc(ref m);
|
|
||||||
|
|
||||||
if (mDetector != null)
|
|
||||||
{
|
|
||||||
mDetector.WndProc(ref m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
|
||||||
this.SuspendLayout();
|
|
||||||
//
|
|
||||||
// label1
|
|
||||||
//
|
|
||||||
this.label1.AutoSize = true;
|
|
||||||
this.label1.Location = new System.Drawing.Point(13, 30);
|
|
||||||
this.label1.Name = "label1";
|
|
||||||
this.label1.Size = new System.Drawing.Size(377, 12);
|
|
||||||
this.label1.TabIndex = 0;
|
|
||||||
this.label1.Text = "This is invisible form. To see DriveDetector code click View Code";
|
|
||||||
//
|
|
||||||
// DetectorForm
|
|
||||||
//
|
|
||||||
this.ClientSize = new System.Drawing.Size(435, 80);
|
|
||||||
this.Controls.Add(this.label1);
|
|
||||||
this.Name = "DetectorForm";
|
|
||||||
this.ResumeLayout(false);
|
|
||||||
this.PerformLayout();
|
|
||||||
|
|
||||||
}
|
|
||||||
} // class DetectorForm
|
|
||||||
|
|
||||||
|
|
||||||
// Delegate for event handler to handle the device events
|
|
||||||
public delegate void DriveDetectorEventHandler(Object sender, DriveDetectorEventArgs e);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Our class for passing in custom arguments to our event handlers
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class DriveDetectorEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public DriveDetectorEventArgs()
|
|
||||||
{
|
|
||||||
Cancel = false;
|
|
||||||
Drive = "";
|
|
||||||
HookQueryRemove = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get/Set the value indicating that the event should be cancelled
|
|
||||||
/// Only in QueryRemove handler.
|
|
||||||
/// </summary>
|
|
||||||
public bool Cancel;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Drive letter for the device which caused this event
|
|
||||||
/// </summary>
|
|
||||||
public string Drive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to true in your DeviceArrived event handler if you wish to receive the
|
|
||||||
/// QueryRemove event for this drive.
|
|
||||||
/// </summary>
|
|
||||||
public bool HookQueryRemove;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Detects insertion or removal of removable drives.
|
|
||||||
/// Use it in 1 or 2 steps:
|
|
||||||
/// 1) Create instance of this class in your project and add handlers for the
|
|
||||||
/// DeviceArrived, DeviceRemoved and QueryRemove events.
|
|
||||||
/// AND (if you do not want drive detector to creaate a hidden form))
|
|
||||||
/// 2) Override WndProc in your form and call DriveDetector's WndProc from there.
|
|
||||||
/// If you do not want to do step 2, just use the DriveDetector constructor without arguments and
|
|
||||||
/// it will create its own invisible form to receive messages from Windows.
|
|
||||||
/// </summary>
|
|
||||||
class DriveDetector : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Events signalized to the client app.
|
|
||||||
/// Add handlers for these events in your form to be notified of removable device events
|
|
||||||
/// </summary>
|
|
||||||
public event DriveDetectorEventHandler DeviceArrived;
|
|
||||||
public event DriveDetectorEventHandler DeviceRemoved;
|
|
||||||
public event DriveDetectorEventHandler QueryRemove;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The easiest way to use DriveDetector.
|
|
||||||
/// It will create hidden form for processing Windows messages about USB drives
|
|
||||||
/// You do not need to override WndProc in your form.
|
|
||||||
/// </summary>
|
|
||||||
public DriveDetector()
|
|
||||||
{
|
|
||||||
DetectorForm frm = new DetectorForm(this);
|
|
||||||
frm.Show(); // will be hidden immediatelly
|
|
||||||
Init(frm, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Alternate constructor.
|
|
||||||
/// Pass in your Form and DriveDetector will not create hidden form.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="control">object which will receive Windows messages.
|
|
||||||
/// Pass "this" as this argument from your form class.</param>
|
|
||||||
public DriveDetector(Control control)
|
|
||||||
{
|
|
||||||
Init(control, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Consructs DriveDetector object setting also path to file which should be opened
|
|
||||||
/// when registering for query remove.
|
|
||||||
/// </summary>
|
|
||||||
///<param name="control">object which will receive Windows messages.
|
|
||||||
/// Pass "this" as this argument from your form class.</param>
|
|
||||||
/// <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened.
|
|
||||||
/// If null, root directory of the drive will be opened. Opening a file is needed for us
|
|
||||||
/// to be able to register for the query remove message. TIP: For files use relative path without drive letter.
|
|
||||||
/// e.g. "SomeFolder\file_on_flash.txt"</param>
|
|
||||||
public DriveDetector(Control control, string FileToOpen)
|
|
||||||
{
|
|
||||||
Init(control, FileToOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// init the DriveDetector object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="intPtr"></param>
|
|
||||||
private void Init(Control control, string fileToOpen)
|
|
||||||
{
|
|
||||||
mFileToOpen = fileToOpen;
|
|
||||||
mFileOnFlash = null;
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
mRecipientHandle = control.Handle;
|
|
||||||
mDirHandle = IntPtr.Zero; // handle to the root directory of the flash drive which we open
|
|
||||||
mCurrentDrive = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value indicating whether the query remove event will be fired.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsQueryHooked
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (mDeviceNotifyHandle == IntPtr.Zero)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets letter of drive which is currently hooked. Empty string if none.
|
|
||||||
/// See also IsQueryHooked.
|
|
||||||
/// </summary>
|
|
||||||
public string HookedDrive
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return mCurrentDrive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file stream for file which this class opened on a drive to be notified
|
|
||||||
/// about it's removal.
|
|
||||||
/// This will be null unless you specified a file to open (DriveDetector opens root directory of the flash drive)
|
|
||||||
/// </summary>
|
|
||||||
public FileStream OpenedFile
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return mFileOnFlash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Hooks specified drive to receive a message when it is being removed.
|
|
||||||
/// This can be achieved also by setting e.HookQueryRemove to true in your
|
|
||||||
/// DeviceArrived event handler.
|
|
||||||
/// By default DriveDetector will open the root directory of the flash drive to obtain notification handle
|
|
||||||
/// from Windows (to learn when the drive is about to be removed).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileOnDrive">Drive letter or relative path to a file on the drive which should be
|
|
||||||
/// used to get a handle - required for registering to receive query remove messages.
|
|
||||||
/// If only drive letter is specified (e.g. "D:\\", root directory of the drive will be opened.</param>
|
|
||||||
/// <returns>true if hooked ok, false otherwise</returns>
|
|
||||||
public bool EnableQueryRemove(string fileOnDrive)
|
|
||||||
{
|
|
||||||
if (fileOnDrive == null || fileOnDrive.Length == 0)
|
|
||||||
throw new ArgumentException("Drive path must be supplied to register for Query remove.");
|
|
||||||
|
|
||||||
if ( fileOnDrive.Length == 2 && fileOnDrive[1] == ':' )
|
|
||||||
fileOnDrive += '\\'; // append "\\" if only drive letter with ":" was passed in.
|
|
||||||
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
// Unregister first...
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Path.GetFileName(fileOnDrive).Length == 0 ||!File.Exists(fileOnDrive))
|
|
||||||
mFileToOpen = null; // use root directory...
|
|
||||||
else
|
|
||||||
mFileToOpen = fileOnDrive;
|
|
||||||
|
|
||||||
RegisterQuery(Path.GetPathRoot(fileOnDrive));
|
|
||||||
if (mDeviceNotifyHandle == IntPtr.Zero)
|
|
||||||
return false; // failed to register
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unhooks any currently hooked drive so that the query remove
|
|
||||||
/// message is not generated for it.
|
|
||||||
/// </summary>
|
|
||||||
public void DisableQueryRemove()
|
|
||||||
{
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unregister and close the file we may have opened on the removable drive.
|
|
||||||
/// Garbage collector will call this method.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region WindowProc
|
|
||||||
/// <summary>
|
|
||||||
/// Message handler which must be called from client form.
|
|
||||||
/// Processes Windows messages and calls event handlers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m"></param>
|
|
||||||
public void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
int devType;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
if (m.Msg == WM_DEVICECHANGE)
|
|
||||||
{
|
|
||||||
// WM_DEVICECHANGE can have several meanings depending on the WParam value...
|
|
||||||
switch (m.WParam.ToInt32())
|
|
||||||
{
|
|
||||||
|
|
||||||
//
|
|
||||||
// New device has just arrived
|
|
||||||
//
|
|
||||||
case DBT_DEVICEARRIVAL:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
DEV_BROADCAST_VOLUME vol;
|
|
||||||
vol = (DEV_BROADCAST_VOLUME)
|
|
||||||
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
|
|
||||||
|
|
||||||
// Get the drive letter
|
|
||||||
c = DriveMaskToLetter(vol.dbcv_unitmask);
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the client event handler
|
|
||||||
//
|
|
||||||
// We should create copy of the event before testing it and
|
|
||||||
// calling the delegate - if any
|
|
||||||
DriveDetectorEventHandler tempDeviceArrived = DeviceArrived;
|
|
||||||
if ( tempDeviceArrived != null )
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = c + ":\\";
|
|
||||||
tempDeviceArrived(this, e);
|
|
||||||
|
|
||||||
// Register for query remove if requested
|
|
||||||
if (e.HookQueryRemove)
|
|
||||||
{
|
|
||||||
// If something is already hooked, unhook it now
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterQuery(c + ":\\");
|
|
||||||
}
|
|
||||||
} // if has event handler
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Device is about to be removed
|
|
||||||
// Any application can cancel the removal
|
|
||||||
//
|
|
||||||
case DBT_DEVICEQUERYREMOVE:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_HANDLE)
|
|
||||||
{
|
|
||||||
// TODO: we could get the handle for which this message is sent
|
|
||||||
// from vol.dbch_handle and compare it against a list of handles for
|
|
||||||
// which we have registered the query remove message (?)
|
|
||||||
//DEV_BROADCAST_HANDLE vol;
|
|
||||||
//vol = (DEV_BROADCAST_HANDLE)
|
|
||||||
// Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
|
|
||||||
// if ( vol.dbch_handle ....
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the event handler in client
|
|
||||||
//
|
|
||||||
DriveDetectorEventHandler tempQuery = QueryRemove;
|
|
||||||
if (tempQuery != null)
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = mCurrentDrive; // drive which is hooked
|
|
||||||
tempQuery(this, e);
|
|
||||||
|
|
||||||
// If the client wants to cancel, let Windows know
|
|
||||||
if (e.Cancel)
|
|
||||||
{
|
|
||||||
m.Result = (IntPtr)BROADCAST_QUERY_DENY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Change 28.10.2007: Unregister the notification, this will
|
|
||||||
// close the handle to file or root directory also.
|
|
||||||
// We have to close it anyway to allow the removal so
|
|
||||||
// even if some other app cancels the removal we would not know about it...
|
|
||||||
RegisterForDeviceChange(false, null); // will also close the mFileOnFlash
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Device has been removed
|
|
||||||
//
|
|
||||||
case DBT_DEVICEREMOVECOMPLETE:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
DEV_BROADCAST_VOLUME vol;
|
|
||||||
vol = (DEV_BROADCAST_VOLUME)
|
|
||||||
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
|
|
||||||
c = DriveMaskToLetter(vol.dbcv_unitmask);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the client event handler
|
|
||||||
//
|
|
||||||
DriveDetectorEventHandler tempDeviceRemoved = DeviceRemoved;
|
|
||||||
if (tempDeviceRemoved != null)
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = c + ":\\";
|
|
||||||
tempDeviceRemoved(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we could unregister the notify handle here if we knew it is the
|
|
||||||
// right drive which has been just removed
|
|
||||||
//RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region Private Area
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New: 28.10.2007 - handle to root directory of flash drive which is opened
|
|
||||||
/// for device notification
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mDirHandle = IntPtr.Zero;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Class which contains also handle to the file opened on the flash drive
|
|
||||||
/// </summary>
|
|
||||||
private FileStream mFileOnFlash = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the file to try to open on the removable drive for query remove registration
|
|
||||||
/// </summary>
|
|
||||||
private string mFileToOpen;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle to file which we keep opened on the drive if query remove message is required by the client
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mDeviceNotifyHandle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle of the window which receives messages from Windows. This will be a form.
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mRecipientHandle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Drive which is currently hooked for query remove
|
|
||||||
/// </summary>
|
|
||||||
private string mCurrentDrive;
|
|
||||||
|
|
||||||
|
|
||||||
// Win32 constants
|
|
||||||
private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
|
|
||||||
private const int DBT_DEVTYP_HANDLE = 6;
|
|
||||||
private const int BROADCAST_QUERY_DENY = 0x424D5144;
|
|
||||||
private const int WM_DEVICECHANGE = 0x0219;
|
|
||||||
private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
|
|
||||||
private const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal)
|
|
||||||
private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
|
|
||||||
private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers for receiving the query remove message for a given drive.
|
|
||||||
/// We need to open a handle on that drive and register with this handle.
|
|
||||||
/// Client can specify this file in mFileToOpen or we will open root directory of the drive
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="drive">drive for which to register. </param>
|
|
||||||
private void RegisterQuery(string drive)
|
|
||||||
{
|
|
||||||
bool register = true;
|
|
||||||
|
|
||||||
if (mFileToOpen == null)
|
|
||||||
{
|
|
||||||
// Change 28.10.2007 - Open the root directory if no file specified - leave mFileToOpen null
|
|
||||||
// If client gave us no file, let's pick one on the drive...
|
|
||||||
//mFileToOpen = GetAnyFile(drive);
|
|
||||||
//if (mFileToOpen.Length == 0)
|
|
||||||
// return; // no file found on the flash drive
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Make sure the path in mFileToOpen contains valid drive
|
|
||||||
// If there is a drive letter in the path, it may be different from the actual
|
|
||||||
// letter assigned to the drive now. We will cut it off and merge the actual drive
|
|
||||||
// with the rest of the path.
|
|
||||||
if (mFileToOpen.Contains(":"))
|
|
||||||
{
|
|
||||||
string tmp = mFileToOpen.Substring(3);
|
|
||||||
string root = Path.GetPathRoot(drive);
|
|
||||||
mFileToOpen = Path.Combine(root, tmp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mFileToOpen = Path.Combine(drive, mFileToOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
|
|
||||||
// Change 28.10.2007 - Open the root directory
|
|
||||||
if (mFileToOpen == null) // open root directory
|
|
||||||
mFileOnFlash = null;
|
|
||||||
else
|
|
||||||
mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// just do not register if the file could not be opened
|
|
||||||
register = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (register)
|
|
||||||
{
|
|
||||||
//RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
|
|
||||||
//mCurrentDrive = drive;
|
|
||||||
// Change 28.10.2007 - Open the root directory
|
|
||||||
if (mFileOnFlash == null)
|
|
||||||
RegisterForDeviceChange(drive);
|
|
||||||
else
|
|
||||||
// old version
|
|
||||||
RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
|
|
||||||
|
|
||||||
mCurrentDrive = drive;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New version which gets the handle automatically for specified directory
|
|
||||||
/// Only for registering! Unregister with the old version of this function...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="register"></param>
|
|
||||||
/// <param name="dirPath">e.g. C:\\dir</param>
|
|
||||||
private void RegisterForDeviceChange(string dirPath)
|
|
||||||
{
|
|
||||||
IntPtr handle = Native.OpenDirectory(dirPath);
|
|
||||||
if (handle == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mDirHandle = handle; // save handle for closing it when unregistering
|
|
||||||
|
|
||||||
// Register for handle
|
|
||||||
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
|
|
||||||
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
||||||
data.dbch_reserved = 0;
|
|
||||||
data.dbch_nameoffset = 0;
|
|
||||||
//data.dbch_data = null;
|
|
||||||
//data.dbch_eventguid = 0;
|
|
||||||
data.dbch_handle = handle;
|
|
||||||
data.dbch_hdevnotify = (IntPtr)0;
|
|
||||||
int size = Marshal.SizeOf(data);
|
|
||||||
data.dbch_size = size;
|
|
||||||
IntPtr buffer = Marshal.AllocHGlobal(size);
|
|
||||||
Marshal.StructureToPtr(data, buffer, true);
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers to be notified when the volume is about to be removed
|
|
||||||
/// This is requierd if you want to get the QUERY REMOVE messages
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="register">true to register, false to unregister</param>
|
|
||||||
/// <param name="fileHandle">handle of a file opened on the removable drive</param>
|
|
||||||
private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle)
|
|
||||||
{
|
|
||||||
if (register)
|
|
||||||
{
|
|
||||||
// Register for handle
|
|
||||||
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
|
|
||||||
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
||||||
data.dbch_reserved = 0;
|
|
||||||
data.dbch_nameoffset = 0;
|
|
||||||
//data.dbch_data = null;
|
|
||||||
//data.dbch_eventguid = 0;
|
|
||||||
data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle;
|
|
||||||
data.dbch_hdevnotify = (IntPtr)0;
|
|
||||||
int size = Marshal.SizeOf(data);
|
|
||||||
data.dbch_size = size;
|
|
||||||
IntPtr buffer = Marshal.AllocHGlobal(size);
|
|
||||||
Marshal.StructureToPtr(data, buffer, true);
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// close the directory handle
|
|
||||||
if (mDirHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Native.CloseDirectoryHandle(mDirHandle);
|
|
||||||
// string er = Marshal.GetLastWin32Error().ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Native.UnregisterDeviceNotification(mDeviceNotifyHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
mDirHandle = IntPtr.Zero;
|
|
||||||
|
|
||||||
mCurrentDrive = "";
|
|
||||||
if (mFileOnFlash != null)
|
|
||||||
{
|
|
||||||
mFileOnFlash.Close();
|
|
||||||
mFileOnFlash = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
|
|
||||||
/// There can actually be more than one drive in the mask but we
|
|
||||||
/// just use the last one in this case.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mask"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static char DriveMaskToLetter(int mask)
|
|
||||||
{
|
|
||||||
char letter;
|
|
||||||
string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
// 1 = A
|
|
||||||
// 2 = B
|
|
||||||
// 4 = C...
|
|
||||||
int cnt = 0;
|
|
||||||
int pom = mask / 2;
|
|
||||||
while (pom != 0)
|
|
||||||
{
|
|
||||||
// while there is any bit set in the mask
|
|
||||||
// shift it to the righ...
|
|
||||||
pom = pom / 2;
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnt < drives.Length)
|
|
||||||
letter = drives[cnt];
|
|
||||||
else
|
|
||||||
letter = '?';
|
|
||||||
|
|
||||||
return letter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 28.10.2007 - no longer needed
|
|
||||||
/// <summary>
|
|
||||||
/// Searches for any file in a given path and returns its full path
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="drive">drive to search</param>
|
|
||||||
/// <returns>path of the file or empty string</returns>
|
|
||||||
private string GetAnyFile(string drive)
|
|
||||||
{
|
|
||||||
string file = "";
|
|
||||||
// First try files in the root
|
|
||||||
string[] files = Directory.GetFiles(drive);
|
|
||||||
if (files.Length == 0)
|
|
||||||
{
|
|
||||||
// if no file in the root, search whole drive
|
|
||||||
files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.Length > 0)
|
|
||||||
file = files[0]; // get the first file
|
|
||||||
|
|
||||||
// return empty string if no file found
|
|
||||||
return file;
|
|
||||||
}*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region Native Win32 API
|
|
||||||
/// <summary>
|
|
||||||
/// WinAPI functions
|
|
||||||
/// </summary>
|
|
||||||
private class Native
|
|
||||||
{
|
|
||||||
// HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
||||||
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
||||||
public static extern uint UnregisterDeviceNotification(IntPtr hHandle);
|
|
||||||
|
|
||||||
//
|
|
||||||
// CreateFile - MSDN
|
|
||||||
const uint GENERIC_READ = 0x80000000;
|
|
||||||
const uint OPEN_EXISTING = 3;
|
|
||||||
const uint FILE_SHARE_READ = 0x00000001;
|
|
||||||
const uint FILE_SHARE_WRITE = 0x00000002;
|
|
||||||
const uint FILE_ATTRIBUTE_NORMAL = 128;
|
|
||||||
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
|
||||||
static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
|
|
||||||
|
|
||||||
|
|
||||||
// should be "static extern unsafe"
|
|
||||||
[DllImport("kernel32", SetLastError = true)]
|
|
||||||
static extern IntPtr CreateFile(
|
|
||||||
string FileName, // file name
|
|
||||||
uint DesiredAccess, // access mode
|
|
||||||
uint ShareMode, // share mode
|
|
||||||
uint SecurityAttributes, // Security Attributes
|
|
||||||
uint CreationDisposition, // how to create
|
|
||||||
uint FlagsAndAttributes, // file attributes
|
|
||||||
int hTemplateFile // handle to template file
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
[DllImport("kernel32", SetLastError = true)]
|
|
||||||
static extern bool CloseHandle(
|
|
||||||
IntPtr hObject // handle to object
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens a directory, returns it's handle or zero.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dirPath">path to the directory, e.g. "C:\\dir"</param>
|
|
||||||
/// <returns>handle to the directory. Close it with CloseHandle().</returns>
|
|
||||||
static public IntPtr OpenDirectory(string dirPath)
|
|
||||||
{
|
|
||||||
// open the existing file for reading
|
|
||||||
IntPtr handle = CreateFile(
|
|
||||||
dirPath,
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
||||||
0,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if ( handle == INVALID_HANDLE_VALUE)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
else
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static bool CloseDirectoryHandle(IntPtr handle)
|
|
||||||
{
|
|
||||||
return CloseHandle(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Structure with information for RegisterDeviceNotification.
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DEV_BROADCAST_HANDLE
|
|
||||||
{
|
|
||||||
public int dbch_size;
|
|
||||||
public int dbch_devicetype;
|
|
||||||
public int dbch_reserved;
|
|
||||||
public IntPtr dbch_handle;
|
|
||||||
public IntPtr dbch_hdevnotify;
|
|
||||||
public Guid dbch_eventguid;
|
|
||||||
public long dbch_nameoffset;
|
|
||||||
//public byte[] dbch_data[1]; // = new byte[1];
|
|
||||||
public byte dbch_data;
|
|
||||||
public byte dbch_data1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct for parameters of the WM_DEVICECHANGE message
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DEV_BROADCAST_VOLUME
|
|
||||||
{
|
|
||||||
public int dbcv_size;
|
|
||||||
public int dbcv_devicetype;
|
|
||||||
public int dbcv_reserved;
|
|
||||||
public int dbcv_unitmask;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
180
Cs_HMI/Project/Dialog/fLog.Designer.cs
generated
180
Cs_HMI/Project/Dialog/fLog.Designer.cs
generated
@@ -31,16 +31,15 @@
|
|||||||
this.rtsys = new arCtl.LogTextBox();
|
this.rtsys = new arCtl.LogTextBox();
|
||||||
this.rtTx = new arCtl.LogTextBox();
|
this.rtTx = new arCtl.LogTextBox();
|
||||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
this.rtAGV = new arCtl.LogTextBox();
|
|
||||||
this.rtBMS = new arCtl.LogTextBox();
|
|
||||||
this.rtXbee = new arCtl.LogTextBox();
|
|
||||||
this.panel1 = new System.Windows.Forms.Panel();
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.rtXbee = new arCtl.LogTextBox();
|
||||||
|
this.titleXBEE = new System.Windows.Forms.Label();
|
||||||
this.panel2 = new System.Windows.Forms.Panel();
|
this.panel2 = new System.Windows.Forms.Panel();
|
||||||
this.panel3 = new System.Windows.Forms.Panel();
|
this.rtBMS = new arCtl.LogTextBox();
|
||||||
|
this.titleBMS = new System.Windows.Forms.Label();
|
||||||
this.panel4 = new System.Windows.Forms.Panel();
|
this.panel4 = new System.Windows.Forms.Panel();
|
||||||
this.label2 = new System.Windows.Forms.Label();
|
this.rtAGV = new arCtl.LogTextBox();
|
||||||
this.label3 = new System.Windows.Forms.Label();
|
this.titleAGV = new System.Windows.Forms.Label();
|
||||||
this.tableLayoutPanel1.SuspendLayout();
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
this.panel2.SuspendLayout();
|
this.panel2.SuspendLayout();
|
||||||
@@ -99,8 +98,7 @@
|
|||||||
this.tableLayoutPanel1.Controls.Add(this.rtsys, 0, 0);
|
this.tableLayoutPanel1.Controls.Add(this.rtsys, 0, 0);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.rtTx, 2, 0);
|
this.tableLayoutPanel1.Controls.Add(this.rtTx, 2, 0);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 1);
|
this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 1);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.panel2, 3, 1);
|
this.tableLayoutPanel1.Controls.Add(this.panel2, 2, 1);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.panel3, 2, 1);
|
|
||||||
this.tableLayoutPanel1.Controls.Add(this.panel4, 0, 1);
|
this.tableLayoutPanel1.Controls.Add(this.panel4, 0, 1);
|
||||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||||
@@ -111,45 +109,15 @@
|
|||||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(681, 495);
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(681, 495);
|
||||||
this.tableLayoutPanel1.TabIndex = 2;
|
this.tableLayoutPanel1.TabIndex = 2;
|
||||||
//
|
//
|
||||||
// rtAGV
|
// panel1
|
||||||
//
|
//
|
||||||
this.rtAGV.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
this.panel1.Controls.Add(this.rtXbee);
|
||||||
this.rtAGV.ColorList = new arCtl.sLogMessageColor[0];
|
this.panel1.Controls.Add(this.titleXBEE);
|
||||||
this.rtAGV.DateFormat = "mm:ss.fff";
|
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.rtAGV.DefaultColor = System.Drawing.Color.LightGray;
|
this.panel1.Location = new System.Drawing.Point(173, 349);
|
||||||
this.rtAGV.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.panel1.Name = "panel1";
|
||||||
this.rtAGV.EnableDisplayTimer = false;
|
this.panel1.Size = new System.Drawing.Size(164, 143);
|
||||||
this.rtAGV.EnableGubunColor = true;
|
this.panel1.TabIndex = 3;
|
||||||
this.rtAGV.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
|
||||||
this.rtAGV.ListFormat = "[{0}] {1}";
|
|
||||||
this.rtAGV.Location = new System.Drawing.Point(0, 14);
|
|
||||||
this.rtAGV.MaxListCount = ((ushort)(1000));
|
|
||||||
this.rtAGV.MaxTextLength = ((uint)(400000u));
|
|
||||||
this.rtAGV.MessageInterval = 50;
|
|
||||||
this.rtAGV.Name = "rtAGV";
|
|
||||||
this.rtAGV.Size = new System.Drawing.Size(164, 129);
|
|
||||||
this.rtAGV.TabIndex = 2;
|
|
||||||
this.rtAGV.Text = "";
|
|
||||||
//
|
|
||||||
// rtBMS
|
|
||||||
//
|
|
||||||
this.rtBMS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
|
||||||
this.rtBMS.ColorList = new arCtl.sLogMessageColor[0];
|
|
||||||
this.rtBMS.DateFormat = "mm:ss.fff";
|
|
||||||
this.rtBMS.DefaultColor = System.Drawing.Color.LightGray;
|
|
||||||
this.rtBMS.Dock = System.Windows.Forms.DockStyle.Fill;
|
|
||||||
this.rtBMS.EnableDisplayTimer = false;
|
|
||||||
this.rtBMS.EnableGubunColor = true;
|
|
||||||
this.rtBMS.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
|
||||||
this.rtBMS.ListFormat = "[{0}] {1}";
|
|
||||||
this.rtBMS.Location = new System.Drawing.Point(0, 14);
|
|
||||||
this.rtBMS.MaxListCount = ((ushort)(1000));
|
|
||||||
this.rtBMS.MaxTextLength = ((uint)(400000u));
|
|
||||||
this.rtBMS.MessageInterval = 50;
|
|
||||||
this.rtBMS.Name = "rtBMS";
|
|
||||||
this.rtBMS.Size = new System.Drawing.Size(165, 129);
|
|
||||||
this.rtBMS.TabIndex = 2;
|
|
||||||
this.rtBMS.Text = "";
|
|
||||||
//
|
//
|
||||||
// rtXbee
|
// rtXbee
|
||||||
//
|
//
|
||||||
@@ -171,73 +139,96 @@
|
|||||||
this.rtXbee.TabIndex = 2;
|
this.rtXbee.TabIndex = 2;
|
||||||
this.rtXbee.Text = "";
|
this.rtXbee.Text = "";
|
||||||
//
|
//
|
||||||
// panel1
|
// titleXBEE
|
||||||
//
|
//
|
||||||
this.panel1.Controls.Add(this.rtXbee);
|
this.titleXBEE.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
this.panel1.Controls.Add(this.label1);
|
this.titleXBEE.Location = new System.Drawing.Point(0, 0);
|
||||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.titleXBEE.Name = "titleXBEE";
|
||||||
this.panel1.Location = new System.Drawing.Point(173, 349);
|
this.titleXBEE.Size = new System.Drawing.Size(164, 14);
|
||||||
this.panel1.Name = "panel1";
|
this.titleXBEE.TabIndex = 0;
|
||||||
this.panel1.Size = new System.Drawing.Size(164, 143);
|
this.titleXBEE.Text = "MC ID";
|
||||||
this.panel1.TabIndex = 3;
|
this.titleXBEE.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
//
|
|
||||||
// label1
|
|
||||||
//
|
|
||||||
this.label1.Dock = System.Windows.Forms.DockStyle.Top;
|
|
||||||
this.label1.Location = new System.Drawing.Point(0, 0);
|
|
||||||
this.label1.Name = "label1";
|
|
||||||
this.label1.Size = new System.Drawing.Size(164, 14);
|
|
||||||
this.label1.TabIndex = 0;
|
|
||||||
this.label1.Text = "MC ID";
|
|
||||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
//
|
//
|
||||||
// panel2
|
// panel2
|
||||||
//
|
//
|
||||||
|
this.tableLayoutPanel1.SetColumnSpan(this.panel2, 2);
|
||||||
this.panel2.Controls.Add(this.rtBMS);
|
this.panel2.Controls.Add(this.rtBMS);
|
||||||
this.panel2.Controls.Add(this.label3);
|
this.panel2.Controls.Add(this.titleBMS);
|
||||||
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.panel2.Location = new System.Drawing.Point(513, 349);
|
this.panel2.Location = new System.Drawing.Point(343, 349);
|
||||||
this.panel2.Name = "panel2";
|
this.panel2.Name = "panel2";
|
||||||
this.panel2.Size = new System.Drawing.Size(165, 143);
|
this.panel2.Size = new System.Drawing.Size(335, 143);
|
||||||
this.panel2.TabIndex = 4;
|
this.panel2.TabIndex = 4;
|
||||||
//
|
//
|
||||||
// panel3
|
// rtBMS
|
||||||
//
|
//
|
||||||
this.panel3.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.rtBMS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
||||||
this.panel3.Location = new System.Drawing.Point(343, 349);
|
this.rtBMS.ColorList = new arCtl.sLogMessageColor[0];
|
||||||
this.panel3.Name = "panel3";
|
this.rtBMS.DateFormat = "mm:ss.fff";
|
||||||
this.panel3.Size = new System.Drawing.Size(164, 143);
|
this.rtBMS.DefaultColor = System.Drawing.Color.LightGray;
|
||||||
this.panel3.TabIndex = 5;
|
this.rtBMS.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.rtBMS.EnableDisplayTimer = false;
|
||||||
|
this.rtBMS.EnableGubunColor = true;
|
||||||
|
this.rtBMS.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||||
|
this.rtBMS.ListFormat = "[{0}] {1}";
|
||||||
|
this.rtBMS.Location = new System.Drawing.Point(0, 14);
|
||||||
|
this.rtBMS.MaxListCount = ((ushort)(1000));
|
||||||
|
this.rtBMS.MaxTextLength = ((uint)(400000u));
|
||||||
|
this.rtBMS.MessageInterval = 50;
|
||||||
|
this.rtBMS.Name = "rtBMS";
|
||||||
|
this.rtBMS.Size = new System.Drawing.Size(335, 129);
|
||||||
|
this.rtBMS.TabIndex = 2;
|
||||||
|
this.rtBMS.Text = "";
|
||||||
|
//
|
||||||
|
// titleBMS
|
||||||
|
//
|
||||||
|
this.titleBMS.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
this.titleBMS.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.titleBMS.Name = "titleBMS";
|
||||||
|
this.titleBMS.Size = new System.Drawing.Size(335, 14);
|
||||||
|
this.titleBMS.TabIndex = 3;
|
||||||
|
this.titleBMS.Text = "BMS";
|
||||||
|
this.titleBMS.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
//
|
//
|
||||||
// panel4
|
// panel4
|
||||||
//
|
//
|
||||||
this.panel4.Controls.Add(this.rtAGV);
|
this.panel4.Controls.Add(this.rtAGV);
|
||||||
this.panel4.Controls.Add(this.label2);
|
this.panel4.Controls.Add(this.titleAGV);
|
||||||
this.panel4.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.panel4.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.panel4.Location = new System.Drawing.Point(3, 349);
|
this.panel4.Location = new System.Drawing.Point(3, 349);
|
||||||
this.panel4.Name = "panel4";
|
this.panel4.Name = "panel4";
|
||||||
this.panel4.Size = new System.Drawing.Size(164, 143);
|
this.panel4.Size = new System.Drawing.Size(164, 143);
|
||||||
this.panel4.TabIndex = 6;
|
this.panel4.TabIndex = 6;
|
||||||
//
|
//
|
||||||
// label2
|
// rtAGV
|
||||||
//
|
//
|
||||||
this.label2.Dock = System.Windows.Forms.DockStyle.Top;
|
this.rtAGV.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
||||||
this.label2.Location = new System.Drawing.Point(0, 0);
|
this.rtAGV.ColorList = new arCtl.sLogMessageColor[0];
|
||||||
this.label2.Name = "label2";
|
this.rtAGV.DateFormat = "mm:ss.fff";
|
||||||
this.label2.Size = new System.Drawing.Size(164, 14);
|
this.rtAGV.DefaultColor = System.Drawing.Color.LightGray;
|
||||||
this.label2.TabIndex = 3;
|
this.rtAGV.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.label2.Text = "AGV";
|
this.rtAGV.EnableDisplayTimer = false;
|
||||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.rtAGV.EnableGubunColor = true;
|
||||||
|
this.rtAGV.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||||
|
this.rtAGV.ListFormat = "[{0}] {1}";
|
||||||
|
this.rtAGV.Location = new System.Drawing.Point(0, 14);
|
||||||
|
this.rtAGV.MaxListCount = ((ushort)(1000));
|
||||||
|
this.rtAGV.MaxTextLength = ((uint)(400000u));
|
||||||
|
this.rtAGV.MessageInterval = 50;
|
||||||
|
this.rtAGV.Name = "rtAGV";
|
||||||
|
this.rtAGV.Size = new System.Drawing.Size(164, 129);
|
||||||
|
this.rtAGV.TabIndex = 2;
|
||||||
|
this.rtAGV.Text = "";
|
||||||
//
|
//
|
||||||
// label3
|
// titleAGV
|
||||||
//
|
//
|
||||||
this.label3.Dock = System.Windows.Forms.DockStyle.Top;
|
this.titleAGV.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
this.label3.Location = new System.Drawing.Point(0, 0);
|
this.titleAGV.Location = new System.Drawing.Point(0, 0);
|
||||||
this.label3.Name = "label3";
|
this.titleAGV.Name = "titleAGV";
|
||||||
this.label3.Size = new System.Drawing.Size(165, 14);
|
this.titleAGV.Size = new System.Drawing.Size(164, 14);
|
||||||
this.label3.TabIndex = 3;
|
this.titleAGV.TabIndex = 3;
|
||||||
this.label3.Text = "BMS";
|
this.titleAGV.Text = "AGV";
|
||||||
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.titleAGV.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
//
|
//
|
||||||
// fLog
|
// fLog
|
||||||
//
|
//
|
||||||
@@ -267,11 +258,10 @@
|
|||||||
private arCtl.LogTextBox rtBMS;
|
private arCtl.LogTextBox rtBMS;
|
||||||
private arCtl.LogTextBox rtXbee;
|
private arCtl.LogTextBox rtXbee;
|
||||||
private System.Windows.Forms.Panel panel1;
|
private System.Windows.Forms.Panel panel1;
|
||||||
private System.Windows.Forms.Label label1;
|
private System.Windows.Forms.Label titleXBEE;
|
||||||
private System.Windows.Forms.Panel panel2;
|
private System.Windows.Forms.Panel panel2;
|
||||||
private System.Windows.Forms.Panel panel3;
|
|
||||||
private System.Windows.Forms.Panel panel4;
|
private System.Windows.Forms.Panel panel4;
|
||||||
private System.Windows.Forms.Label label3;
|
private System.Windows.Forms.Label titleBMS;
|
||||||
private System.Windows.Forms.Label label2;
|
private System.Windows.Forms.Label titleAGV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,9 @@ namespace Project.Dialog
|
|||||||
|
|
||||||
private void fLog_Load(object sender, EventArgs e)
|
private void fLog_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
this.label1.Text = $"XBEE:{PUB.setting.XBE_ID}";
|
this.titleXBEE.Text = $"XBEE({PUB.setting.Port_XBE},ID:{PUB.setting.XBE_ID})";
|
||||||
|
this.titleAGV.Text = $"AGV({PUB.setting.Port_AGV}:{PUB.setting.Baud_AGV})";
|
||||||
|
this.titleBMS.Text = $"BMS({PUB.setting.Port_BAT}:{PUB.setting.Baud_BAT})";
|
||||||
var colorlist = new arCtl.sLogMessageColor[]
|
var colorlist = new arCtl.sLogMessageColor[]
|
||||||
{
|
{
|
||||||
new arCtl.sLogMessageColor("NOR",Color.Black),
|
new arCtl.sLogMessageColor("NOR",Color.Black),
|
||||||
|
|||||||
683
Cs_HMI/Project/Dialog/fSystem.Designer.cs
generated
683
Cs_HMI/Project/Dialog/fSystem.Designer.cs
generated
@@ -28,530 +28,307 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.lbMsg = new arCtl.arLabel();
|
this.btOpenDir = new System.Windows.Forms.Button();
|
||||||
this.arLabel4 = new arCtl.arLabel();
|
this.arLabel4 = new System.Windows.Forms.Button();
|
||||||
this.arLabel9 = new arCtl.arLabel();
|
this.btShutdown = new System.Windows.Forms.Button();
|
||||||
this.arLabel10 = new arCtl.arLabel();
|
this.btRestart = new System.Windows.Forms.Button();
|
||||||
this.arLabel1 = new arCtl.arLabel();
|
this.btStartMenu = new System.Windows.Forms.Button();
|
||||||
this.arLabel6 = new arCtl.arLabel();
|
this.btTaskMgr = new System.Windows.Forms.Button();
|
||||||
this.arLabel2 = new arCtl.arLabel();
|
this.btProcessList = new System.Windows.Forms.Button();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
this.label2 = new System.Windows.Forms.Label();
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
this.arLabel3 = new arCtl.arLabel();
|
this.btEmulator = new System.Windows.Forms.Button();
|
||||||
this.arLabel5 = new arCtl.arLabel();
|
this.btMakePatch = new System.Windows.Forms.Button();
|
||||||
this.arLabel7 = new arCtl.arLabel();
|
this.btAutoRestart = new System.Windows.Forms.Button();
|
||||||
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.button1 = new System.Windows.Forms.Button();
|
||||||
|
this.button2 = new System.Windows.Forms.Button();
|
||||||
|
this.button3 = new System.Windows.Forms.Button();
|
||||||
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// lbMsg
|
// btOpenDir
|
||||||
//
|
//
|
||||||
this.lbMsg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btOpenDir.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.lbMsg.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
|
this.btOpenDir.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.lbMsg.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btOpenDir.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.lbMsg.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btOpenDir.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.lbMsg.BorderColorOver = System.Drawing.Color.Red;
|
this.btOpenDir.ForeColor = System.Drawing.Color.White;
|
||||||
this.lbMsg.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btOpenDir.Location = new System.Drawing.Point(5, 5);
|
||||||
this.lbMsg.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btOpenDir.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.lbMsg.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btOpenDir.Name = "btOpenDir";
|
||||||
this.lbMsg.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btOpenDir.Size = new System.Drawing.Size(201, 79);
|
||||||
this.lbMsg.ForeColor = System.Drawing.Color.White;
|
this.btOpenDir.TabIndex = 2;
|
||||||
this.lbMsg.GradientEnable = true;
|
this.btOpenDir.Text = "폴더열기";
|
||||||
this.lbMsg.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btOpenDir.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.lbMsg.GradientRepeatBG = true;
|
this.btOpenDir.Click += new System.EventHandler(this.lbMsg_Click);
|
||||||
this.lbMsg.isButton = true;
|
|
||||||
this.lbMsg.Location = new System.Drawing.Point(9, 8);
|
|
||||||
this.lbMsg.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.lbMsg.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.lbMsg.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.lbMsg.msg = null;
|
|
||||||
this.lbMsg.Name = "lbMsg";
|
|
||||||
this.lbMsg.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.lbMsg.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.lbMsg.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.lbMsg.ProgressEnable = false;
|
|
||||||
this.lbMsg.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.lbMsg.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.lbMsg.ProgressMax = 100F;
|
|
||||||
this.lbMsg.ProgressMin = 0F;
|
|
||||||
this.lbMsg.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.lbMsg.ProgressValue = 0F;
|
|
||||||
this.lbMsg.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.lbMsg.Sign = "";
|
|
||||||
this.lbMsg.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.lbMsg.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.lbMsg.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.lbMsg.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.lbMsg.TabIndex = 2;
|
|
||||||
this.lbMsg.Text = "폴더열기";
|
|
||||||
this.lbMsg.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.lbMsg.TextShadow = true;
|
|
||||||
this.lbMsg.TextVisible = true;
|
|
||||||
this.lbMsg.Click += new System.EventHandler(this.lbMsg_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel4
|
// arLabel4
|
||||||
//
|
//
|
||||||
this.arLabel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.arLabel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel4.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
|
|
||||||
this.arLabel4.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel4.BorderColor = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel4.BorderColorOver = System.Drawing.Color.Red;
|
|
||||||
this.arLabel4.BorderSize = new System.Windows.Forms.Padding(1);
|
|
||||||
this.arLabel4.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
|
||||||
this.arLabel4.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.arLabel4.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
|
this.arLabel4.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.arLabel4.Font = new System.Drawing.Font("Consolas", 12F);
|
this.arLabel4.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel4.ForeColor = System.Drawing.Color.White;
|
this.arLabel4.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel4.GradientEnable = true;
|
this.arLabel4.Location = new System.Drawing.Point(10, 412);
|
||||||
this.arLabel4.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
|
||||||
this.arLabel4.GradientRepeatBG = true;
|
|
||||||
this.arLabel4.isButton = true;
|
|
||||||
this.arLabel4.Location = new System.Drawing.Point(9, 387);
|
|
||||||
this.arLabel4.Margin = new System.Windows.Forms.Padding(5);
|
this.arLabel4.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel4.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel4.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel4.msg = null;
|
|
||||||
this.arLabel4.Name = "arLabel4";
|
this.arLabel4.Name = "arLabel4";
|
||||||
this.arLabel4.ProgressBorderColor = System.Drawing.Color.Black;
|
this.arLabel4.Size = new System.Drawing.Size(633, 51);
|
||||||
this.arLabel4.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel4.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel4.ProgressEnable = false;
|
|
||||||
this.arLabel4.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel4.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel4.ProgressMax = 100F;
|
|
||||||
this.arLabel4.ProgressMin = 0F;
|
|
||||||
this.arLabel4.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel4.ProgressValue = 0F;
|
|
||||||
this.arLabel4.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel4.Sign = "";
|
|
||||||
this.arLabel4.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel4.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel4.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel4.Size = new System.Drawing.Size(503, 51);
|
|
||||||
this.arLabel4.TabIndex = 2;
|
this.arLabel4.TabIndex = 2;
|
||||||
this.arLabel4.Text = "닫기";
|
this.arLabel4.Text = "닫기";
|
||||||
this.arLabel4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.arLabel4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel4.TextShadow = true;
|
|
||||||
this.arLabel4.TextVisible = true;
|
|
||||||
this.arLabel4.Click += new System.EventHandler(this.arLabel4_Click);
|
this.arLabel4.Click += new System.EventHandler(this.arLabel4_Click);
|
||||||
//
|
//
|
||||||
// arLabel9
|
// btShutdown
|
||||||
//
|
//
|
||||||
this.arLabel9.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btShutdown.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel9.BackColor2 = System.Drawing.Color.Red;
|
this.btShutdown.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel9.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btShutdown.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel9.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btShutdown.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel9.BorderColorOver = System.Drawing.Color.Red;
|
this.btShutdown.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel9.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btShutdown.Location = new System.Drawing.Point(5, 94);
|
||||||
this.arLabel9.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btShutdown.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel9.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btShutdown.Name = "btShutdown";
|
||||||
this.arLabel9.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btShutdown.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel9.ForeColor = System.Drawing.Color.White;
|
this.btShutdown.TabIndex = 2;
|
||||||
this.arLabel9.GradientEnable = true;
|
this.btShutdown.Text = "시스템 종료";
|
||||||
this.arLabel9.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btShutdown.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel9.GradientRepeatBG = true;
|
this.btShutdown.Click += new System.EventHandler(this.arLabel9_Click);
|
||||||
this.arLabel9.isButton = true;
|
|
||||||
this.arLabel9.Location = new System.Drawing.Point(9, 112);
|
|
||||||
this.arLabel9.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel9.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel9.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel9.msg = null;
|
|
||||||
this.arLabel9.Name = "arLabel9";
|
|
||||||
this.arLabel9.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel9.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel9.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel9.ProgressEnable = false;
|
|
||||||
this.arLabel9.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel9.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel9.ProgressMax = 100F;
|
|
||||||
this.arLabel9.ProgressMin = 0F;
|
|
||||||
this.arLabel9.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel9.ProgressValue = 0F;
|
|
||||||
this.arLabel9.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel9.Sign = "";
|
|
||||||
this.arLabel9.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel9.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel9.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel9.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel9.TabIndex = 2;
|
|
||||||
this.arLabel9.Text = "시스템 종료";
|
|
||||||
this.arLabel9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel9.TextShadow = true;
|
|
||||||
this.arLabel9.TextVisible = true;
|
|
||||||
this.arLabel9.Click += new System.EventHandler(this.arLabel9_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel10
|
// btRestart
|
||||||
//
|
//
|
||||||
this.arLabel10.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btRestart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel10.BackColor2 = System.Drawing.Color.DarkBlue;
|
this.btRestart.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel10.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btRestart.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel10.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btRestart.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel10.BorderColorOver = System.Drawing.Color.Red;
|
this.btRestart.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel10.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btRestart.Location = new System.Drawing.Point(216, 94);
|
||||||
this.arLabel10.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btRestart.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel10.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btRestart.Name = "btRestart";
|
||||||
this.arLabel10.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btRestart.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel10.ForeColor = System.Drawing.Color.White;
|
this.btRestart.TabIndex = 2;
|
||||||
this.arLabel10.GradientEnable = true;
|
this.btRestart.Text = "시스템 재시작";
|
||||||
this.arLabel10.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btRestart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel10.GradientRepeatBG = true;
|
this.btRestart.Click += new System.EventHandler(this.arLabel10_Click);
|
||||||
this.arLabel10.isButton = true;
|
|
||||||
this.arLabel10.Location = new System.Drawing.Point(178, 112);
|
|
||||||
this.arLabel10.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel10.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel10.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel10.msg = null;
|
|
||||||
this.arLabel10.Name = "arLabel10";
|
|
||||||
this.arLabel10.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel10.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel10.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel10.ProgressEnable = false;
|
|
||||||
this.arLabel10.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel10.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel10.ProgressMax = 100F;
|
|
||||||
this.arLabel10.ProgressMin = 0F;
|
|
||||||
this.arLabel10.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel10.ProgressValue = 0F;
|
|
||||||
this.arLabel10.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel10.Sign = "";
|
|
||||||
this.arLabel10.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel10.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel10.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel10.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel10.TabIndex = 2;
|
|
||||||
this.arLabel10.Text = "시스템 재시작";
|
|
||||||
this.arLabel10.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel10.TextShadow = true;
|
|
||||||
this.arLabel10.TextVisible = true;
|
|
||||||
this.arLabel10.Click += new System.EventHandler(this.arLabel10_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel1
|
// btStartMenu
|
||||||
//
|
//
|
||||||
this.arLabel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btStartMenu.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
|
this.btStartMenu.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel1.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btStartMenu.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel1.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btStartMenu.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel1.BorderColorOver = System.Drawing.Color.Red;
|
this.btStartMenu.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel1.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btStartMenu.Location = new System.Drawing.Point(427, 5);
|
||||||
this.arLabel1.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btStartMenu.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel1.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btStartMenu.Name = "btStartMenu";
|
||||||
this.arLabel1.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btStartMenu.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel1.ForeColor = System.Drawing.Color.White;
|
this.btStartMenu.TabIndex = 2;
|
||||||
this.arLabel1.GradientEnable = true;
|
this.btStartMenu.Text = "시작메뉴";
|
||||||
this.arLabel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btStartMenu.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel1.GradientRepeatBG = true;
|
this.btStartMenu.Click += new System.EventHandler(this.arLabel1_Click);
|
||||||
this.arLabel1.isButton = true;
|
|
||||||
this.arLabel1.Location = new System.Drawing.Point(347, 8);
|
|
||||||
this.arLabel1.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel1.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel1.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel1.msg = null;
|
|
||||||
this.arLabel1.Name = "arLabel1";
|
|
||||||
this.arLabel1.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel1.ProgressEnable = false;
|
|
||||||
this.arLabel1.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel1.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel1.ProgressMax = 100F;
|
|
||||||
this.arLabel1.ProgressMin = 0F;
|
|
||||||
this.arLabel1.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel1.ProgressValue = 0F;
|
|
||||||
this.arLabel1.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel1.Sign = "";
|
|
||||||
this.arLabel1.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel1.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel1.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel1.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel1.TabIndex = 2;
|
|
||||||
this.arLabel1.Text = "시작메뉴";
|
|
||||||
this.arLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel1.TextShadow = true;
|
|
||||||
this.arLabel1.TextVisible = true;
|
|
||||||
this.arLabel1.Click += new System.EventHandler(this.arLabel1_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel6
|
// btTaskMgr
|
||||||
//
|
//
|
||||||
this.arLabel6.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btTaskMgr.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel6.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
|
this.btTaskMgr.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel6.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btTaskMgr.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel6.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btTaskMgr.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel6.BorderColorOver = System.Drawing.Color.Red;
|
this.btTaskMgr.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel6.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btTaskMgr.Location = new System.Drawing.Point(216, 5);
|
||||||
this.arLabel6.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btTaskMgr.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel6.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btTaskMgr.Name = "btTaskMgr";
|
||||||
this.arLabel6.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btTaskMgr.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel6.ForeColor = System.Drawing.Color.White;
|
this.btTaskMgr.TabIndex = 2;
|
||||||
this.arLabel6.GradientEnable = true;
|
this.btTaskMgr.Text = "작업관리자";
|
||||||
this.arLabel6.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btTaskMgr.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel6.GradientRepeatBG = true;
|
this.btTaskMgr.Click += new System.EventHandler(this.arLabel6_Click);
|
||||||
this.arLabel6.isButton = true;
|
|
||||||
this.arLabel6.Location = new System.Drawing.Point(178, 8);
|
|
||||||
this.arLabel6.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel6.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel6.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel6.msg = null;
|
|
||||||
this.arLabel6.Name = "arLabel6";
|
|
||||||
this.arLabel6.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel6.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel6.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel6.ProgressEnable = false;
|
|
||||||
this.arLabel6.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel6.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel6.ProgressMax = 100F;
|
|
||||||
this.arLabel6.ProgressMin = 0F;
|
|
||||||
this.arLabel6.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel6.ProgressValue = 0F;
|
|
||||||
this.arLabel6.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel6.Sign = "";
|
|
||||||
this.arLabel6.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel6.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel6.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel6.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel6.TabIndex = 2;
|
|
||||||
this.arLabel6.Text = "작업관리자";
|
|
||||||
this.arLabel6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel6.TextShadow = true;
|
|
||||||
this.arLabel6.TextVisible = true;
|
|
||||||
this.arLabel6.Click += new System.EventHandler(this.arLabel6_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel2
|
// btProcessList
|
||||||
//
|
//
|
||||||
this.arLabel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btProcessList.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel2.BackColor2 = System.Drawing.Color.DarkBlue;
|
this.btProcessList.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel2.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btProcessList.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel2.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btProcessList.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel2.BorderColorOver = System.Drawing.Color.Red;
|
this.btProcessList.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel2.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btProcessList.Location = new System.Drawing.Point(427, 94);
|
||||||
this.arLabel2.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btProcessList.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel2.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btProcessList.Name = "btProcessList";
|
||||||
this.arLabel2.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btProcessList.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel2.ForeColor = System.Drawing.Color.White;
|
this.btProcessList.TabIndex = 2;
|
||||||
this.arLabel2.GradientEnable = true;
|
this.btProcessList.Text = "Process List";
|
||||||
this.arLabel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btProcessList.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel2.GradientRepeatBG = true;
|
this.btProcessList.Click += new System.EventHandler(this.arLabel2_Click);
|
||||||
this.arLabel2.isButton = true;
|
|
||||||
this.arLabel2.Location = new System.Drawing.Point(347, 112);
|
|
||||||
this.arLabel2.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel2.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel2.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel2.msg = null;
|
|
||||||
this.arLabel2.Name = "arLabel2";
|
|
||||||
this.arLabel2.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel2.ProgressEnable = false;
|
|
||||||
this.arLabel2.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel2.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel2.ProgressMax = 100F;
|
|
||||||
this.arLabel2.ProgressMin = 0F;
|
|
||||||
this.arLabel2.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel2.ProgressValue = 0F;
|
|
||||||
this.arLabel2.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel2.Sign = "";
|
|
||||||
this.arLabel2.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel2.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel2.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel2.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel2.TabIndex = 2;
|
|
||||||
this.arLabel2.Text = "Process List";
|
|
||||||
this.arLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel2.TextShadow = true;
|
|
||||||
this.arLabel2.TextVisible = true;
|
|
||||||
this.arLabel2.Click += new System.EventHandler(this.arLabel2_Click);
|
|
||||||
//
|
//
|
||||||
// label1
|
// label1
|
||||||
//
|
//
|
||||||
|
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.label1.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
this.label1.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this.label1.ForeColor = System.Drawing.Color.White;
|
this.label1.ForeColor = System.Drawing.Color.White;
|
||||||
this.label1.Location = new System.Drawing.Point(9, 327);
|
this.label1.Location = new System.Drawing.Point(10, 389);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(515, 23);
|
this.label1.Size = new System.Drawing.Size(633, 23);
|
||||||
this.label1.TabIndex = 3;
|
this.label1.TabIndex = 3;
|
||||||
this.label1.Text = "label1";
|
this.label1.Text = "label1";
|
||||||
//
|
//
|
||||||
// label2
|
// label2
|
||||||
//
|
//
|
||||||
|
this.label2.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.label2.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
this.label2.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this.label2.ForeColor = System.Drawing.Color.White;
|
this.label2.ForeColor = System.Drawing.Color.White;
|
||||||
this.label2.Location = new System.Drawing.Point(9, 359);
|
this.label2.Location = new System.Drawing.Point(10, 366);
|
||||||
this.label2.Name = "label2";
|
this.label2.Name = "label2";
|
||||||
this.label2.Size = new System.Drawing.Size(515, 23);
|
this.label2.Size = new System.Drawing.Size(633, 23);
|
||||||
this.label2.TabIndex = 3;
|
this.label2.TabIndex = 3;
|
||||||
this.label2.Text = "label1";
|
this.label2.Text = "label1";
|
||||||
//
|
//
|
||||||
// arLabel3
|
// btEmulator
|
||||||
//
|
//
|
||||||
this.arLabel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btEmulator.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel3.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
|
this.btEmulator.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel3.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btEmulator.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel3.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btEmulator.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel3.BorderColorOver = System.Drawing.Color.Red;
|
this.btEmulator.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel3.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btEmulator.Location = new System.Drawing.Point(5, 183);
|
||||||
this.arLabel3.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btEmulator.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel3.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btEmulator.Name = "btEmulator";
|
||||||
this.arLabel3.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btEmulator.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel3.ForeColor = System.Drawing.Color.White;
|
this.btEmulator.TabIndex = 4;
|
||||||
this.arLabel3.GradientEnable = true;
|
this.btEmulator.Text = "Emulator";
|
||||||
this.arLabel3.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btEmulator.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel3.GradientRepeatBG = true;
|
this.btEmulator.Click += new System.EventHandler(this.arLabel3_Click);
|
||||||
this.arLabel3.isButton = true;
|
|
||||||
this.arLabel3.Location = new System.Drawing.Point(9, 216);
|
|
||||||
this.arLabel3.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel3.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel3.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel3.msg = null;
|
|
||||||
this.arLabel3.Name = "arLabel3";
|
|
||||||
this.arLabel3.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel3.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel3.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel3.ProgressEnable = false;
|
|
||||||
this.arLabel3.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel3.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel3.ProgressMax = 100F;
|
|
||||||
this.arLabel3.ProgressMin = 0F;
|
|
||||||
this.arLabel3.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel3.ProgressValue = 0F;
|
|
||||||
this.arLabel3.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel3.Sign = "";
|
|
||||||
this.arLabel3.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel3.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel3.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel3.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel3.TabIndex = 4;
|
|
||||||
this.arLabel3.Text = "Emulator";
|
|
||||||
this.arLabel3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel3.TextShadow = true;
|
|
||||||
this.arLabel3.TextVisible = true;
|
|
||||||
this.arLabel3.Click += new System.EventHandler(this.arLabel3_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel5
|
// btMakePatch
|
||||||
//
|
//
|
||||||
this.arLabel5.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btMakePatch.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel5.BackColor2 = System.Drawing.Color.Pink;
|
this.btMakePatch.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel5.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btMakePatch.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel5.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btMakePatch.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel5.BorderColorOver = System.Drawing.Color.Red;
|
this.btMakePatch.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel5.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btMakePatch.Location = new System.Drawing.Point(216, 183);
|
||||||
this.arLabel5.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btMakePatch.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel5.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btMakePatch.Name = "btMakePatch";
|
||||||
this.arLabel5.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btMakePatch.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel5.ForeColor = System.Drawing.Color.White;
|
this.btMakePatch.TabIndex = 5;
|
||||||
this.arLabel5.GradientEnable = true;
|
this.btMakePatch.Text = "패치파일 생성";
|
||||||
this.arLabel5.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btMakePatch.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel5.GradientRepeatBG = true;
|
this.btMakePatch.Click += new System.EventHandler(this.arLabel5_Click);
|
||||||
this.arLabel5.isButton = true;
|
|
||||||
this.arLabel5.Location = new System.Drawing.Point(178, 216);
|
|
||||||
this.arLabel5.Margin = new System.Windows.Forms.Padding(5);
|
|
||||||
this.arLabel5.MouseDownColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel5.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
|
||||||
this.arLabel5.msg = null;
|
|
||||||
this.arLabel5.Name = "arLabel5";
|
|
||||||
this.arLabel5.ProgressBorderColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel5.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
|
||||||
this.arLabel5.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
|
||||||
this.arLabel5.ProgressEnable = false;
|
|
||||||
this.arLabel5.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
|
||||||
this.arLabel5.ProgressForeColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel5.ProgressMax = 100F;
|
|
||||||
this.arLabel5.ProgressMin = 0F;
|
|
||||||
this.arLabel5.ProgressPadding = new System.Windows.Forms.Padding(0);
|
|
||||||
this.arLabel5.ProgressValue = 0F;
|
|
||||||
this.arLabel5.ShadowColor = System.Drawing.Color.Black;
|
|
||||||
this.arLabel5.Sign = "";
|
|
||||||
this.arLabel5.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
|
||||||
this.arLabel5.SignColor = System.Drawing.Color.Yellow;
|
|
||||||
this.arLabel5.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
|
||||||
this.arLabel5.Size = new System.Drawing.Size(165, 100);
|
|
||||||
this.arLabel5.TabIndex = 5;
|
|
||||||
this.arLabel5.Text = "패치파일 생성";
|
|
||||||
this.arLabel5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
|
||||||
this.arLabel5.TextShadow = true;
|
|
||||||
this.arLabel5.TextVisible = true;
|
|
||||||
this.arLabel5.Click += new System.EventHandler(this.arLabel5_Click);
|
|
||||||
//
|
//
|
||||||
// arLabel7
|
// btAutoRestart
|
||||||
//
|
//
|
||||||
this.arLabel7.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
this.btAutoRestart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
|
||||||
this.arLabel7.BackColor2 = System.Drawing.Color.DarkBlue;
|
this.btAutoRestart.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.arLabel7.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
|
this.btAutoRestart.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel7.BorderColor = System.Drawing.Color.LightSkyBlue;
|
this.btAutoRestart.Font = new System.Drawing.Font("Consolas", 12F);
|
||||||
this.arLabel7.BorderColorOver = System.Drawing.Color.Red;
|
this.btAutoRestart.ForeColor = System.Drawing.Color.White;
|
||||||
this.arLabel7.BorderSize = new System.Windows.Forms.Padding(1);
|
this.btAutoRestart.Location = new System.Drawing.Point(427, 183);
|
||||||
this.arLabel7.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
|
this.btAutoRestart.Margin = new System.Windows.Forms.Padding(5);
|
||||||
this.arLabel7.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.btAutoRestart.Name = "btAutoRestart";
|
||||||
this.arLabel7.Font = new System.Drawing.Font("Consolas", 12F);
|
this.btAutoRestart.Size = new System.Drawing.Size(201, 79);
|
||||||
this.arLabel7.ForeColor = System.Drawing.Color.White;
|
this.btAutoRestart.TabIndex = 6;
|
||||||
this.arLabel7.GradientEnable = true;
|
this.btAutoRestart.Text = "자동 재시작";
|
||||||
this.arLabel7.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
|
this.btAutoRestart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.arLabel7.GradientRepeatBG = true;
|
this.btAutoRestart.Click += new System.EventHandler(this.arLabel7_Click);
|
||||||
this.arLabel7.isButton = true;
|
//
|
||||||
this.arLabel7.Location = new System.Drawing.Point(347, 216);
|
// tableLayoutPanel1
|
||||||
this.arLabel7.Margin = new System.Windows.Forms.Padding(5);
|
//
|
||||||
this.arLabel7.MouseDownColor = System.Drawing.Color.Yellow;
|
this.tableLayoutPanel1.ColumnCount = 3;
|
||||||
this.arLabel7.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
this.arLabel7.msg = null;
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
this.arLabel7.Name = "arLabel7";
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
this.arLabel7.ProgressBorderColor = System.Drawing.Color.Black;
|
this.tableLayoutPanel1.Controls.Add(this.btOpenDir, 0, 0);
|
||||||
this.arLabel7.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
|
this.tableLayoutPanel1.Controls.Add(this.btAutoRestart, 2, 2);
|
||||||
this.arLabel7.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
|
this.tableLayoutPanel1.Controls.Add(this.btTaskMgr, 1, 0);
|
||||||
this.arLabel7.ProgressEnable = false;
|
this.tableLayoutPanel1.Controls.Add(this.btMakePatch, 1, 2);
|
||||||
this.arLabel7.ProgressFont = new System.Drawing.Font("Consolas", 10F);
|
this.tableLayoutPanel1.Controls.Add(this.btStartMenu, 2, 0);
|
||||||
this.arLabel7.ProgressForeColor = System.Drawing.Color.Black;
|
this.tableLayoutPanel1.Controls.Add(this.btEmulator, 0, 2);
|
||||||
this.arLabel7.ProgressMax = 100F;
|
this.tableLayoutPanel1.Controls.Add(this.btShutdown, 0, 1);
|
||||||
this.arLabel7.ProgressMin = 0F;
|
this.tableLayoutPanel1.Controls.Add(this.btRestart, 1, 1);
|
||||||
this.arLabel7.ProgressPadding = new System.Windows.Forms.Padding(0);
|
this.tableLayoutPanel1.Controls.Add(this.btProcessList, 2, 1);
|
||||||
this.arLabel7.ProgressValue = 0F;
|
this.tableLayoutPanel1.Controls.Add(this.button1, 0, 3);
|
||||||
this.arLabel7.ShadowColor = System.Drawing.Color.Black;
|
this.tableLayoutPanel1.Controls.Add(this.button2, 1, 3);
|
||||||
this.arLabel7.Sign = "";
|
this.tableLayoutPanel1.Controls.Add(this.button3, 2, 3);
|
||||||
this.arLabel7.SignAlign = System.Drawing.ContentAlignment.BottomRight;
|
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.arLabel7.SignColor = System.Drawing.Color.Yellow;
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(10, 10);
|
||||||
this.arLabel7.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
|
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
this.arLabel7.Size = new System.Drawing.Size(165, 100);
|
this.tableLayoutPanel1.RowCount = 4;
|
||||||
this.arLabel7.TabIndex = 6;
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
|
||||||
this.arLabel7.Text = "자동 재시작";
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
|
||||||
this.arLabel7.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
|
||||||
this.arLabel7.TextShadow = true;
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
|
||||||
this.arLabel7.TextVisible = true;
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(633, 356);
|
||||||
this.arLabel7.Click += new System.EventHandler(this.arLabel7_Click);
|
this.tableLayoutPanel1.TabIndex = 7;
|
||||||
|
//
|
||||||
|
// button1
|
||||||
|
//
|
||||||
|
this.button1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button1.Location = new System.Drawing.Point(3, 270);
|
||||||
|
this.button1.Name = "button1";
|
||||||
|
this.button1.Size = new System.Drawing.Size(205, 83);
|
||||||
|
this.button1.TabIndex = 7;
|
||||||
|
this.button1.Text = "Map Editor";
|
||||||
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
|
this.button1.Click += new System.EventHandler(this.button1_Click_1);
|
||||||
|
//
|
||||||
|
// button2
|
||||||
|
//
|
||||||
|
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button2.Enabled = false;
|
||||||
|
this.button2.Location = new System.Drawing.Point(214, 270);
|
||||||
|
this.button2.Name = "button2";
|
||||||
|
this.button2.Size = new System.Drawing.Size(205, 83);
|
||||||
|
this.button2.TabIndex = 7;
|
||||||
|
this.button2.Text = "---";
|
||||||
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// button3
|
||||||
|
//
|
||||||
|
this.button3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button3.Enabled = false;
|
||||||
|
this.button3.Location = new System.Drawing.Point(425, 270);
|
||||||
|
this.button3.Name = "button3";
|
||||||
|
this.button3.Size = new System.Drawing.Size(205, 83);
|
||||||
|
this.button3.TabIndex = 7;
|
||||||
|
this.button3.Text = "---";
|
||||||
|
this.button3.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// fSystem
|
// fSystem
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||||
this.ClientSize = new System.Drawing.Size(519, 451);
|
this.ClientSize = new System.Drawing.Size(653, 473);
|
||||||
this.Controls.Add(this.arLabel7);
|
this.Controls.Add(this.tableLayoutPanel1);
|
||||||
this.Controls.Add(this.arLabel5);
|
|
||||||
this.Controls.Add(this.arLabel3);
|
|
||||||
this.Controls.Add(this.label2);
|
this.Controls.Add(this.label2);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.label1);
|
||||||
this.Controls.Add(this.arLabel4);
|
this.Controls.Add(this.arLabel4);
|
||||||
this.Controls.Add(this.lbMsg);
|
|
||||||
this.Controls.Add(this.arLabel9);
|
|
||||||
this.Controls.Add(this.arLabel10);
|
|
||||||
this.Controls.Add(this.arLabel1);
|
|
||||||
this.Controls.Add(this.arLabel2);
|
|
||||||
this.Controls.Add(this.arLabel6);
|
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.Name = "fSystem";
|
this.Name = "fSystem";
|
||||||
|
this.Padding = new System.Windows.Forms.Padding(10);
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
this.Text = "fSystem";
|
this.Text = "fSystem";
|
||||||
this.Load += new System.EventHandler(this.fSystem_Load);
|
this.Load += new System.EventHandler(this.fSystem_Load);
|
||||||
|
this.tableLayoutPanel1.ResumeLayout(false);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
private arCtl.arLabel lbMsg;
|
private System.Windows.Forms.Button btOpenDir;
|
||||||
private arCtl.arLabel arLabel4;
|
private System.Windows.Forms.Button arLabel4;
|
||||||
private arCtl.arLabel arLabel6;
|
private System.Windows.Forms.Button btTaskMgr;
|
||||||
private arCtl.arLabel arLabel9;
|
private System.Windows.Forms.Button btShutdown;
|
||||||
private arCtl.arLabel arLabel10;
|
private System.Windows.Forms.Button btRestart;
|
||||||
private arCtl.arLabel arLabel1;
|
private System.Windows.Forms.Button btStartMenu;
|
||||||
private arCtl.arLabel arLabel2;
|
private System.Windows.Forms.Button btProcessList;
|
||||||
private System.Windows.Forms.Label label1;
|
private System.Windows.Forms.Label label1;
|
||||||
private System.Windows.Forms.Label label2;
|
private System.Windows.Forms.Label label2;
|
||||||
private arCtl.arLabel arLabel3;
|
private System.Windows.Forms.Button btEmulator;
|
||||||
private arCtl.arLabel arLabel5;
|
private System.Windows.Forms.Button btMakePatch;
|
||||||
private arCtl.arLabel arLabel7;
|
private System.Windows.Forms.Button btAutoRestart;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||||
|
private System.Windows.Forms.Button button1;
|
||||||
|
private System.Windows.Forms.Button button2;
|
||||||
|
private System.Windows.Forms.Button button3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,5 +154,13 @@ namespace Project.Dialog
|
|||||||
{
|
{
|
||||||
PUB.SystemReboot(5,true);
|
PUB.SystemReboot(5,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void button1_Click_1(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//mapeditor
|
||||||
|
var fn = new System.IO.FileInfo(@".\test\AGVMapEditor.exe");
|
||||||
|
if (fn.Exists == false) return;
|
||||||
|
UTIL.RunExplorer(fn.FullName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
239
Cs_HMI/Project/Dialog/fXbeeSetting.Designer.cs
generated
Normal file
239
Cs_HMI/Project/Dialog/fXbeeSetting.Designer.cs
generated
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
namespace Project.Dialog
|
||||||
|
{
|
||||||
|
partial class fXbeeSetting
|
||||||
|
{
|
||||||
|
/// <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.components = new System.ComponentModel.Container();
|
||||||
|
this.button1 = new System.Windows.Forms.Button();
|
||||||
|
this.tbBaud = new System.Windows.Forms.TextBox();
|
||||||
|
this.tbPortName = new System.Windows.Forms.ComboBox();
|
||||||
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
|
this.button2 = new System.Windows.Forms.Button();
|
||||||
|
this.btmy = new System.Windows.Forms.Button();
|
||||||
|
this.tbmy = new System.Windows.Forms.TextBox();
|
||||||
|
this.btch = new System.Windows.Forms.Button();
|
||||||
|
this.tbch = new System.Windows.Forms.TextBox();
|
||||||
|
this.btpand = new System.Windows.Forms.Button();
|
||||||
|
this.tbpanid = new System.Windows.Forms.TextBox();
|
||||||
|
this.panel2 = new System.Windows.Forms.Panel();
|
||||||
|
this.rtXbee = new arCtl.LogTextBox();
|
||||||
|
this.serialPort1 = new System.IO.Ports.SerialPort(this.components);
|
||||||
|
this.panel1.SuspendLayout();
|
||||||
|
this.panel2.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// button1
|
||||||
|
//
|
||||||
|
this.button1.Location = new System.Drawing.Point(25, 45);
|
||||||
|
this.button1.Name = "button1";
|
||||||
|
this.button1.Size = new System.Drawing.Size(227, 26);
|
||||||
|
this.button1.TabIndex = 0;
|
||||||
|
this.button1.Text = "open/close";
|
||||||
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
|
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||||
|
//
|
||||||
|
// tbBaud
|
||||||
|
//
|
||||||
|
this.tbBaud.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.tbBaud.Location = new System.Drawing.Point(152, 13);
|
||||||
|
this.tbBaud.Name = "tbBaud";
|
||||||
|
this.tbBaud.Size = new System.Drawing.Size(100, 26);
|
||||||
|
this.tbBaud.TabIndex = 2;
|
||||||
|
this.tbBaud.Text = "9600";
|
||||||
|
this.tbBaud.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||||
|
//
|
||||||
|
// tbPortName
|
||||||
|
//
|
||||||
|
this.tbPortName.Font = new System.Drawing.Font("굴림", 12F);
|
||||||
|
this.tbPortName.FormattingEnabled = true;
|
||||||
|
this.tbPortName.Location = new System.Drawing.Point(25, 15);
|
||||||
|
this.tbPortName.Name = "tbPortName";
|
||||||
|
this.tbPortName.Size = new System.Drawing.Size(121, 24);
|
||||||
|
this.tbPortName.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// panel1
|
||||||
|
//
|
||||||
|
this.panel1.Controls.Add(this.button2);
|
||||||
|
this.panel1.Controls.Add(this.btmy);
|
||||||
|
this.panel1.Controls.Add(this.tbmy);
|
||||||
|
this.panel1.Controls.Add(this.btch);
|
||||||
|
this.panel1.Controls.Add(this.tbch);
|
||||||
|
this.panel1.Controls.Add(this.btpand);
|
||||||
|
this.panel1.Controls.Add(this.tbpanid);
|
||||||
|
this.panel1.Controls.Add(this.tbPortName);
|
||||||
|
this.panel1.Controls.Add(this.button1);
|
||||||
|
this.panel1.Controls.Add(this.tbBaud);
|
||||||
|
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
this.panel1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.panel1.Name = "panel1";
|
||||||
|
this.panel1.Size = new System.Drawing.Size(580, 118);
|
||||||
|
this.panel1.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// button2
|
||||||
|
//
|
||||||
|
this.button2.Location = new System.Drawing.Point(293, 15);
|
||||||
|
this.button2.Name = "button2";
|
||||||
|
this.button2.Size = new System.Drawing.Size(75, 89);
|
||||||
|
this.button2.TabIndex = 10;
|
||||||
|
this.button2.Text = "Read Setting";
|
||||||
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||||
|
//
|
||||||
|
// btmy
|
||||||
|
//
|
||||||
|
this.btmy.Location = new System.Drawing.Point(480, 78);
|
||||||
|
this.btmy.Name = "btmy";
|
||||||
|
this.btmy.Size = new System.Drawing.Size(86, 26);
|
||||||
|
this.btmy.TabIndex = 9;
|
||||||
|
this.btmy.Text = "My";
|
||||||
|
this.btmy.UseVisualStyleBackColor = true;
|
||||||
|
this.btmy.Click += new System.EventHandler(this.btmy_Click);
|
||||||
|
//
|
||||||
|
// tbmy
|
||||||
|
//
|
||||||
|
this.tbmy.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.tbmy.Location = new System.Drawing.Point(374, 78);
|
||||||
|
this.tbmy.Name = "tbmy";
|
||||||
|
this.tbmy.Size = new System.Drawing.Size(100, 26);
|
||||||
|
this.tbmy.TabIndex = 8;
|
||||||
|
this.tbmy.Text = "9600";
|
||||||
|
this.tbmy.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||||
|
//
|
||||||
|
// btch
|
||||||
|
//
|
||||||
|
this.btch.Location = new System.Drawing.Point(480, 47);
|
||||||
|
this.btch.Name = "btch";
|
||||||
|
this.btch.Size = new System.Drawing.Size(86, 26);
|
||||||
|
this.btch.TabIndex = 7;
|
||||||
|
this.btch.Text = "Channel";
|
||||||
|
this.btch.UseVisualStyleBackColor = true;
|
||||||
|
this.btch.Click += new System.EventHandler(this.btch_Click);
|
||||||
|
//
|
||||||
|
// tbch
|
||||||
|
//
|
||||||
|
this.tbch.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.tbch.Location = new System.Drawing.Point(374, 47);
|
||||||
|
this.tbch.Name = "tbch";
|
||||||
|
this.tbch.Size = new System.Drawing.Size(100, 26);
|
||||||
|
this.tbch.TabIndex = 6;
|
||||||
|
this.tbch.Text = "9600";
|
||||||
|
this.tbch.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||||
|
//
|
||||||
|
// btpand
|
||||||
|
//
|
||||||
|
this.btpand.Location = new System.Drawing.Point(480, 15);
|
||||||
|
this.btpand.Name = "btpand";
|
||||||
|
this.btpand.Size = new System.Drawing.Size(86, 26);
|
||||||
|
this.btpand.TabIndex = 5;
|
||||||
|
this.btpand.Text = "PanID";
|
||||||
|
this.btpand.UseVisualStyleBackColor = true;
|
||||||
|
this.btpand.Click += new System.EventHandler(this.btpand_Click);
|
||||||
|
//
|
||||||
|
// tbpanid
|
||||||
|
//
|
||||||
|
this.tbpanid.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.tbpanid.Location = new System.Drawing.Point(374, 15);
|
||||||
|
this.tbpanid.Name = "tbpanid";
|
||||||
|
this.tbpanid.Size = new System.Drawing.Size(100, 26);
|
||||||
|
this.tbpanid.TabIndex = 4;
|
||||||
|
this.tbpanid.Text = "9600";
|
||||||
|
this.tbpanid.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||||
|
//
|
||||||
|
// panel2
|
||||||
|
//
|
||||||
|
this.panel2.Controls.Add(this.rtXbee);
|
||||||
|
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.panel2.Location = new System.Drawing.Point(0, 118);
|
||||||
|
this.panel2.Name = "panel2";
|
||||||
|
this.panel2.Size = new System.Drawing.Size(580, 338);
|
||||||
|
this.panel2.TabIndex = 5;
|
||||||
|
//
|
||||||
|
// rtXbee
|
||||||
|
//
|
||||||
|
this.rtXbee.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
||||||
|
this.rtXbee.ColorList = new arCtl.sLogMessageColor[0];
|
||||||
|
this.rtXbee.DateFormat = "mm:ss.fff";
|
||||||
|
this.rtXbee.DefaultColor = System.Drawing.Color.LightGray;
|
||||||
|
this.rtXbee.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.rtXbee.EnableDisplayTimer = false;
|
||||||
|
this.rtXbee.EnableGubunColor = true;
|
||||||
|
this.rtXbee.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||||
|
this.rtXbee.ListFormat = "[{0}] {1}";
|
||||||
|
this.rtXbee.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.rtXbee.MaxListCount = ((ushort)(1000));
|
||||||
|
this.rtXbee.MaxTextLength = ((uint)(400000u));
|
||||||
|
this.rtXbee.MessageInterval = 50;
|
||||||
|
this.rtXbee.Name = "rtXbee";
|
||||||
|
this.rtXbee.Size = new System.Drawing.Size(580, 338);
|
||||||
|
this.rtXbee.TabIndex = 2;
|
||||||
|
this.rtXbee.Text = "";
|
||||||
|
//
|
||||||
|
// serialPort1
|
||||||
|
//
|
||||||
|
this.serialPort1.ReadTimeout = 1000;
|
||||||
|
this.serialPort1.WriteTimeout = 1000;
|
||||||
|
//
|
||||||
|
// fXbeeSetting
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(580, 456);
|
||||||
|
this.Controls.Add(this.panel2);
|
||||||
|
this.Controls.Add(this.panel1);
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "fXbeeSetting";
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
|
this.Text = "Form1";
|
||||||
|
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.fXbeeSetting_FormClosed);
|
||||||
|
this.Load += new System.EventHandler(this.fXbeeSetting_Load);
|
||||||
|
this.panel1.ResumeLayout(false);
|
||||||
|
this.panel1.PerformLayout();
|
||||||
|
this.panel2.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button button1;
|
||||||
|
private System.Windows.Forms.TextBox tbBaud;
|
||||||
|
private System.Windows.Forms.ComboBox tbPortName;
|
||||||
|
private System.Windows.Forms.Panel panel1;
|
||||||
|
private System.Windows.Forms.Panel panel2;
|
||||||
|
private arCtl.LogTextBox rtXbee;
|
||||||
|
private System.Windows.Forms.Button btpand;
|
||||||
|
private System.Windows.Forms.TextBox tbpanid;
|
||||||
|
private System.Windows.Forms.Button btmy;
|
||||||
|
private System.Windows.Forms.TextBox tbmy;
|
||||||
|
private System.Windows.Forms.Button btch;
|
||||||
|
private System.Windows.Forms.TextBox tbch;
|
||||||
|
private System.Windows.Forms.Button button2;
|
||||||
|
private System.IO.Ports.SerialPort serialPort1;
|
||||||
|
}
|
||||||
|
}
|
||||||
193
Cs_HMI/Project/Dialog/fXbeeSetting.cs
Normal file
193
Cs_HMI/Project/Dialog/fXbeeSetting.cs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
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;
|
||||||
|
using AR;
|
||||||
|
using COMM;
|
||||||
|
namespace Project.Dialog
|
||||||
|
{
|
||||||
|
public partial class fXbeeSetting : Form
|
||||||
|
{
|
||||||
|
public fXbeeSetting()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
VAR.BOOL[eVarBool.DISABLE_AUTOCONN_XBEE] = true;
|
||||||
|
PUB.XBE.Close();
|
||||||
|
this.serialPort1.DataReceived += SerialPort1_DataReceived;
|
||||||
|
}
|
||||||
|
private void fXbeeSetting_Load(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
this.tbPortName.Items.Clear();
|
||||||
|
foreach (var item in System.IO.Ports.SerialPort.GetPortNames())
|
||||||
|
{
|
||||||
|
this.tbPortName.Items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tbPortName.Text = PUB.setting.Port_XBE;
|
||||||
|
this.tbBaud.Text = PUB.setting.Baud_XBE.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
void showlog(string Message)
|
||||||
|
{
|
||||||
|
if (rtXbee.Visible)
|
||||||
|
{
|
||||||
|
rtXbee.AddMsg(DateTime.Now, "NORMAL", Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showlog(arCtl.LogTextBox rtRx, DateTime LogTime, string TypeStr, string Message)
|
||||||
|
{
|
||||||
|
if (rtRx.Visible)
|
||||||
|
{
|
||||||
|
rtRx.AddMsg(LogTime, TypeStr, Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void button1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (this.serialPort1.IsOpen)
|
||||||
|
{
|
||||||
|
serialPort1.Close();
|
||||||
|
showlog("closed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serialPort1.PortName = tbPortName.Text;
|
||||||
|
serialPort1.BaudRate = int.Parse(tbBaud.Text);
|
||||||
|
serialPort1.Open();
|
||||||
|
showlog("open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fXbeeSetting_FormClosed(object sender, FormClosedEventArgs e)
|
||||||
|
{
|
||||||
|
VAR.BOOL[eVarBool.DISABLE_AUTOCONN_XBEE] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private volatile bool isCommandExecuting = false;
|
||||||
|
|
||||||
|
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
if (isCommandExecuting) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string data = serialPort1.ReadExisting();
|
||||||
|
var hexdata = System.Text.Encoding.Default.GetBytes(data);
|
||||||
|
var hexstr = string.Join(" ", hexdata.Select(t => t.ToString("X2")));
|
||||||
|
if (!string.IsNullOrEmpty(data))
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
showlog($"RxAsync: {hexstr}");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Cmd(string cmd, int timeout = 1000)
|
||||||
|
{
|
||||||
|
isCommandExecuting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!serialPort1.IsOpen) return "Error: Port Closed";
|
||||||
|
|
||||||
|
serialPort1.DiscardInBuffer();
|
||||||
|
serialPort1.Write(cmd);
|
||||||
|
|
||||||
|
System.Threading.Thread.Sleep(20);
|
||||||
|
serialPort1.ReadTimeout = timeout;
|
||||||
|
string res = serialPort1.ReadTo("\r");
|
||||||
|
System.Threading.Thread.Sleep(20);
|
||||||
|
showlog($"Tx:{cmd.Trim()}, Rx:{res}");
|
||||||
|
|
||||||
|
//명령수신호 10ms 대기후 다음 명령을 전송
|
||||||
|
System.Threading.Thread.Sleep(20);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
showlog($"Err: {ex.Message}");
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isCommandExecuting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void btpand_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//각명령마다 회신을 확인하고 다음명령을 실행해야함
|
||||||
|
//명령수신호 10ms 대기후 다음 명령을 전송
|
||||||
|
//명령을 설정하면 응답은 OK\d 형태로 입력된다.
|
||||||
|
var cmds = new string[] {
|
||||||
|
"+++",
|
||||||
|
$"ATID{tbpanid.Text}\r" ,
|
||||||
|
$"ATCN\r"};
|
||||||
|
|
||||||
|
foreach (var cmd in cmds)
|
||||||
|
{
|
||||||
|
if(!Cmd(cmd).Contains("OK"))
|
||||||
|
{
|
||||||
|
showlog("FAIL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btch_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var cmds = new string[] {
|
||||||
|
"+++",
|
||||||
|
$"ATCH{tbch.Text}\r" ,
|
||||||
|
$"ATCN\r"};
|
||||||
|
foreach (var cmd in cmds)
|
||||||
|
{
|
||||||
|
if (!Cmd(cmd).Contains("OK"))
|
||||||
|
{
|
||||||
|
showlog("FAIL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btmy_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var cmds = new string[] {
|
||||||
|
"+++",
|
||||||
|
$"ATMY{tbmy.Text}\r" ,
|
||||||
|
$"ATCN\r"};
|
||||||
|
foreach (var cmd in cmds)
|
||||||
|
{
|
||||||
|
if (!Cmd(cmd).Contains("OK"))
|
||||||
|
{
|
||||||
|
showlog("FAIL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//read all(id,ch,my)
|
||||||
|
if (Cmd("+++").Contains("OK"))
|
||||||
|
{
|
||||||
|
var id = Cmd("ATID\r");
|
||||||
|
var ch = Cmd("ATCH\r");
|
||||||
|
var my = Cmd("ATMY\r");
|
||||||
|
Cmd("ATCN\r");
|
||||||
|
this.BeginInvoke(new Action(() => {
|
||||||
|
this.tbpanid.Text = id;
|
||||||
|
this.tbch.Text = ch;
|
||||||
|
this.tbmy.Text = my;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
Cs_HMI/Project/Dialog/fXbeeSetting.resx
Normal file
123
Cs_HMI/Project/Dialog/fXbeeSetting.resx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?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="serialPort1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
@@ -8,7 +8,11 @@ using System.Media;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
#if SPEECH
|
||||||
using Microsoft.Speech.Synthesis;
|
using Microsoft.Speech.Synthesis;
|
||||||
|
#endif
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -27,7 +31,12 @@ namespace Project
|
|||||||
public static bool AutRebootAlreay = false;
|
public static bool AutRebootAlreay = false;
|
||||||
public static bool DriveSpeed = false;
|
public static bool DriveSpeed = false;
|
||||||
public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas;
|
public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas;
|
||||||
public static List<MapNode> _mapNodes;
|
//public static List<MapNode> _mapNodes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 다음 작업 명령 (PickOn/PickOff)
|
||||||
|
/// </summary>
|
||||||
|
public static ENIGProtocol.AGVCommandHE NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 가상 AGV (시뮬레이션용)
|
/// 가상 AGV (시뮬레이션용)
|
||||||
@@ -69,8 +78,9 @@ namespace Project
|
|||||||
/// 디버그모니터용 소켓(데이터를 전송만 한다)
|
/// 디버그모니터용 소켓(데이터를 전송만 한다)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Device.Socket sock_debug;
|
public static Device.Socket sock_debug;
|
||||||
|
#if SPEECH
|
||||||
private static SpeechSynthesizer voice;
|
private static SpeechSynthesizer voice;
|
||||||
|
#endif
|
||||||
public static SoundPlayer mplayer;
|
public static SoundPlayer mplayer;
|
||||||
|
|
||||||
[DllImport("winmm.dll")]
|
[DllImport("winmm.dll")]
|
||||||
@@ -94,18 +104,33 @@ namespace Project
|
|||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Speak(string m, Boolean force = false, bool addlog = true)
|
/// <summary>
|
||||||
|
/// 스피커를 통한 음성을 출력합니다
|
||||||
|
/// 출력되는 음성은 기본로그에도 자동 포함됩니다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="m"></param>
|
||||||
|
/// <param name="force"></param>
|
||||||
|
/// <param name="addlog">로그에도 출력 합니다</param>
|
||||||
|
public static void Speak(string m, Boolean force = false, bool addlog = true,string logcate="")
|
||||||
{
|
{
|
||||||
if (force == false && PUB.setting.Enable_Speak == false)
|
if (force == false && PUB.setting.Enable_Speak == false)
|
||||||
{
|
{
|
||||||
Console.WriteLine("speech disabled");
|
Console.WriteLine("speech disabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if SPEECH
|
||||||
if (force)
|
if (force)
|
||||||
voice.SpeakAsyncCancelAll();
|
voice.SpeakAsyncCancelAll();
|
||||||
if (voice.State == SynthesizerState.Ready)
|
if (voice.State == SynthesizerState.Ready)
|
||||||
voice.SpeakAsync(m);
|
voice.SpeakAsync(m);
|
||||||
if (addlog) PUB.log.Add("SPEAK", m);
|
if (addlog)
|
||||||
|
{
|
||||||
|
if (logcate.isEmpty()) logcate = "SPEAK";
|
||||||
|
PUB.log.Add(logcate, m);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PUB.log.Add($"스피치컴파일상수OFF");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -118,7 +143,7 @@ namespace Project
|
|||||||
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] &&
|
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] &&
|
||||||
VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false &&
|
VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false &&
|
||||||
PUB.BMS.Current_Level > PUB.setting.ChargeEmergencyLevel &&
|
PUB.BMS.Current_Level > PUB.setting.ChargeEmergencyLevel &&
|
||||||
PUB.sm.RunStep != StateMachine.ERunStep.GOUP &&
|
PUB.sm.RunStep != StateMachine.ERunStep.BUFFER_OUT &&
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_DOWN] == false &&
|
VAR.BOOL[eVarBool.WAIT_COVER_DOWN] == false &&
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_UP] == false)
|
VAR.BOOL[eVarBool.WAIT_COVER_UP] == false)
|
||||||
{
|
{
|
||||||
@@ -217,7 +242,7 @@ namespace Project
|
|||||||
PUB.mplayer.SoundLocation = PUB.setting.musicfile;
|
PUB.mplayer.SoundLocation = PUB.setting.musicfile;
|
||||||
SetVolume(PUB.setting.musicvol);
|
SetVolume(PUB.setting.musicvol);
|
||||||
}
|
}
|
||||||
|
#if SPEECH
|
||||||
voice = new SpeechSynthesizer();
|
voice = new SpeechSynthesizer();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -245,7 +270,7 @@ namespace Project
|
|||||||
// 음성 설정 실패 시 기본값 사용
|
// 음성 설정 실패 시 기본값 사용
|
||||||
}
|
}
|
||||||
voice.SetOutputToDefaultAudioDevice();
|
voice.SetOutputToDefaultAudioDevice();
|
||||||
|
#endif
|
||||||
var file_version = System.IO.Path.Combine(UTIL.CurrentPath, "version.txt");
|
var file_version = System.IO.Path.Combine(UTIL.CurrentPath, "version.txt");
|
||||||
if (System.IO.File.Exists(file_version))
|
if (System.IO.File.Exists(file_version))
|
||||||
{
|
{
|
||||||
@@ -382,6 +407,9 @@ namespace Project
|
|||||||
return Lang.AGV연결실패;
|
return Lang.AGV연결실패;
|
||||||
case eECode.PLCCONN:
|
case eECode.PLCCONN:
|
||||||
return Lang.PLC통신실패;
|
return Lang.PLC통신실패;
|
||||||
|
case eECode.MESSAGE_ERROR:
|
||||||
|
if (values.Length > 0) return values[0].ToString();
|
||||||
|
return "Message Error";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return err.ToString();
|
return err.ToString();
|
||||||
@@ -582,15 +610,46 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region VirtualAGV 실제 데이터 동기화
|
#region VirtualAGV 실제 데이터 동기화
|
||||||
|
public static MapNode FindByNodeID(string nodeidx)
|
||||||
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
|
if (_mapNodes == null || _mapNodes.Any() == false) return null;
|
||||||
|
if (nodeidx.isEmpty()) return null;
|
||||||
|
return _mapNodes.Where(t => t.Id.Equals(nodeidx)).FirstOrDefault();
|
||||||
|
}
|
||||||
|
public static MapNode FindByRFID(string rfidValue)
|
||||||
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
|
if (_mapNodes == null || _mapNodes.Any() == false) return null;
|
||||||
|
if (rfidValue.isEmpty()) return null;
|
||||||
|
return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault();
|
||||||
|
}
|
||||||
|
public static List<MapNode> FindByNodeAlias(string alias)
|
||||||
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
|
if (_mapNodes == null || _mapNodes.Any() == false) return null;
|
||||||
|
if (alias.isEmpty()) return null;
|
||||||
|
var lst = _mapNodes.Where(t => t.AliasName.Equals(alias));
|
||||||
|
if (lst.Any() == false) return null;
|
||||||
|
return lst.ToList();
|
||||||
|
}
|
||||||
|
public static List<MapNode> FindByNodeType(AGVNavigationCore.Models.MapNode type)
|
||||||
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
|
if (_mapNodes == null || _mapNodes.Any() == false) return null;
|
||||||
|
var lst = _mapNodes.Where(t => t.Type.Equals(type));
|
||||||
|
if (lst.Any() == false) return null;
|
||||||
|
return lst.ToList();
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RFID 읽기 시 해당 노드 위치로 AGV 업데이트
|
/// RFID 읽기 시 해당 노드 위치로 AGV 업데이트
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rfidId">읽은 RFID ID</param>
|
/// <param name="rfidId">읽은 RFID ID</param>
|
||||||
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
|
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
|
||||||
/// <returns>업데이트 성공 여부</returns>
|
/// <returns>업데이트 성공 여부</returns>
|
||||||
public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward)
|
public static bool UpdateAGVFromRFID(ushort rfidId, AgvDirection motorDirection = AgvDirection.Forward)
|
||||||
{
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
if (_virtualAGV == null || _mapNodes == null) return false;
|
if (_virtualAGV == null || _mapNodes == null) return false;
|
||||||
|
|
||||||
// RFID에 해당하는 노드 찾기
|
// RFID에 해당하는 노드 찾기
|
||||||
@@ -600,7 +659,7 @@ namespace Project
|
|||||||
_virtualAGV.SetPosition(node, motorDirection);
|
_virtualAGV.SetPosition(node, motorDirection);
|
||||||
RefreshAGVCanvas();
|
RefreshAGVCanvas();
|
||||||
|
|
||||||
log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.NodeId} 위치 업데이트 (방향: {motorDirection})");
|
log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.Id} 위치 업데이트 (방향: {motorDirection})");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,9 +675,10 @@ namespace Project
|
|||||||
/// <returns>업데이트 성공 여부</returns>
|
/// <returns>업데이트 성공 여부</returns>
|
||||||
public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward)
|
public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward)
|
||||||
{
|
{
|
||||||
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
if (_virtualAGV == null || _mapNodes == null) return false;
|
if (_virtualAGV == null || _mapNodes == null) return false;
|
||||||
|
|
||||||
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
|
var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
_virtualAGV.SetPosition(node, motorDirection);
|
_virtualAGV.SetPosition(node, motorDirection);
|
||||||
@@ -675,7 +735,7 @@ namespace Project
|
|||||||
{
|
{
|
||||||
if (_virtualAGV == null) return;
|
if (_virtualAGV == null) return;
|
||||||
|
|
||||||
_virtualAGV.BatteryLevel = batteryLevel;
|
_virtualAGV.SetBatteryLevel(batteryLevel);
|
||||||
RefreshAGVCanvas();
|
RefreshAGVCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,41 +750,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 AGV의 노드 ID 가져오기
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>현재 노드 ID</returns>
|
|
||||||
public static string GetCurrentAGVNodeId()
|
|
||||||
{
|
|
||||||
return _virtualAGV?.CurrentNodeId ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 AGV 위치 가져오기
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>현재 위치</returns>
|
|
||||||
public static Point GetCurrentAGVPosition()
|
|
||||||
{
|
|
||||||
return _virtualAGV?.CurrentPosition ?? Point.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 AGV 방향 가져오기
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>현재 방향</returns>
|
|
||||||
public static AgvDirection GetCurrentAGVDirection()
|
|
||||||
{
|
|
||||||
return _virtualAGV?.CurrentDirection ?? AgvDirection.Forward;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 AGV 상태 가져오기
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>현재 상태</returns>
|
|
||||||
public static AGVState GetCurrentAGVState()
|
|
||||||
{
|
|
||||||
return _virtualAGV?.CurrentState ?? AGVState.Idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -19,20 +19,6 @@ namespace Project
|
|||||||
|
|
||||||
private void _SM_RUN(Boolean isFirst, TimeSpan stepTime)
|
private void _SM_RUN(Boolean isFirst, TimeSpan stepTime)
|
||||||
{
|
{
|
||||||
//중단기능이 동작이라면 처리하지 않는다.
|
|
||||||
if (PUB.sm.bPause)
|
|
||||||
{
|
|
||||||
System.Threading.Thread.Sleep(200);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//가동불가 조건 확인
|
|
||||||
if (CheckStopCondition() == false)
|
|
||||||
{
|
|
||||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//HW 연결오류
|
//HW 연결오류
|
||||||
if (PUB.AGV.IsOpen == false)
|
if (PUB.AGV.IsOpen == false)
|
||||||
{
|
{
|
||||||
@@ -41,17 +27,16 @@ namespace Project
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//이머전시상태라면 stop 처리한다.
|
//가동불가 조건 확인
|
||||||
if (PUB.AGV.error.Emergency &&
|
if (CheckStopCondition() == false) return;
|
||||||
PUB.AGV.system1.agv_stop == true &&
|
|
||||||
PUB.AGV.system1.stop_by_front_detect == false)
|
//중단기능이 동작이라면 처리하지 않는다.
|
||||||
|
if (PUB.sm.bPause)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.비상정지로인해작업을중단합니다);
|
System.Threading.Thread.Sleep(200);
|
||||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//스텝이 변경되었다면?
|
//스텝이 변경되었다면?
|
||||||
if (PUB.sm.RunStep != PUB.sm.RunStepNew)
|
if (PUB.sm.RunStep != PUB.sm.RunStepNew)
|
||||||
{
|
{
|
||||||
@@ -84,7 +69,7 @@ namespace Project
|
|||||||
if (PUB.AGV.system1.stop_by_front_detect == true)
|
if (PUB.AGV.system1.stop_by_front_detect == true)
|
||||||
{
|
{
|
||||||
var tsSpeak = DateTime.Now - LastSpeakTime;
|
var tsSpeak = DateTime.Now - LastSpeakTime;
|
||||||
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
|
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
||||||
LastSpeakTime = DateTime.Now;
|
LastSpeakTime = DateTime.Now;
|
||||||
@@ -92,14 +77,74 @@ namespace Project
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//선로이탈감지
|
||||||
|
if (PUB.AGV.error.runerror_by_no_magent_line == true)
|
||||||
|
{
|
||||||
|
var tsSpeak = DateTime.Now - LastSpeakTime;
|
||||||
|
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.선로를이탈했습니다);
|
||||||
|
LastSpeakTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다
|
||||||
|
if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return;
|
||||||
|
|
||||||
//나머지 상황체크
|
//나머지 상황체크
|
||||||
switch (PUB.sm.RunStep)
|
switch (PUB.sm.RunStep)
|
||||||
{
|
{
|
||||||
|
case ERunStep.GOHOME:
|
||||||
|
if (_SM_RUN_GOHOME(runStepisFirst, PUB.sm.GetRunSteptime) == true)
|
||||||
|
{
|
||||||
|
PUB.log.Add($"홈 이동이 완료되어 준비상태로 전환합니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ERunStep.GOTO: //목적지까지 이동하는 경우
|
case ERunStep.GOTO: //목적지까지 이동하는 경우
|
||||||
_SM_RUN_GOTO(runStepisFirst, PUB.sm.GetRunSteptime);
|
if (_SM_RUN_GOTO(runStepisFirst, PUB.sm.GetRunSteptime) == true)
|
||||||
|
{
|
||||||
|
//목적지가 BUFFER라면 버퍼투입대기위치까지 완료했다는 시그널을 보낸다.
|
||||||
|
var target = PUB._virtualAGV.TargetNode;
|
||||||
|
PUB.log.Add($"목적지({target.RfidId}) 도착완료 타입:{target.Type}, 출발지:{PUB._virtualAGV.StartNode.RfidId}");
|
||||||
|
|
||||||
|
switch (target.StationType)
|
||||||
|
{
|
||||||
|
case AGVNavigationCore.Models.StationType.Buffer:
|
||||||
|
//현재위치가 마지막경로의 NODEID와 일치해야한다
|
||||||
|
if (PUB._virtualAGV.CurrentPath == null)
|
||||||
|
{
|
||||||
|
PUB.log.AddAT("목적지 버퍼이동완료 했지만 상세경로가 없습니다");
|
||||||
|
PUB.XBE.BufferInComplete = false;
|
||||||
|
PUB.XBE.BufferOutComplete = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault();
|
||||||
|
if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNode.Id))
|
||||||
|
{
|
||||||
|
//버퍼진입전 노드에 도착완료했따
|
||||||
|
PUB.XBE.BufferInReady = true;
|
||||||
|
PUB.XBE.BufferReadyError = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//마지막위치가 아닌 다른 위치에 있으니 버퍼 작업을 할 수없다
|
||||||
|
PUB.log.AddAT("목적지 버퍼이동완료 했지만 마지막 노드가 아닙니다");
|
||||||
|
PUB.XBE.BufferInReady = false;
|
||||||
|
PUB.XBE.BufferReadyError = true;
|
||||||
|
}
|
||||||
|
PUB.XBE.BufferInComplete = false;
|
||||||
|
PUB.XBE.BufferOutComplete = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.None;
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ERunStep.MARKSTROPB: //후진방향으로 마크스탑
|
case ERunStep.MARKSTOPB: //후진방향으로 마크스탑
|
||||||
case ERunStep.MARKSTOPF: //전진방향으로 마크스탑
|
case ERunStep.MARKSTOPF: //전진방향으로 마크스탑
|
||||||
|
|
||||||
//이동중이지 않다면 먼저 이동을 진행한다
|
//이동중이지 않다면 먼저 이동을 진행한다
|
||||||
@@ -172,423 +217,173 @@ namespace Project
|
|||||||
PUB.Result.TargetPos = ePosition.QC;
|
PUB.Result.TargetPos = ePosition.QC;
|
||||||
}
|
}
|
||||||
PUB.Result.CurrentPosCW = "1";
|
PUB.Result.CurrentPosCW = "1";
|
||||||
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
|
|
||||||
PUB.log.AddAT("충전 해제로 홈으로 이동");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ERunStep.GOUP: //상차이동
|
|
||||||
if (_SM_RUN_GOUP(runStepisFirst, PUB.sm.GetRunSteptime))
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.상차작업이완료되었습니다);
|
|
||||||
|
|
||||||
//230601
|
|
||||||
// if (PUB.Result != null && PUB.sm != null)
|
|
||||||
// EEMStatus.AddEEDBSQL(PUB.sm.Step, PUB.sm.RunStep.ToString(), PUB.Result.TargetPos.ToString());
|
|
||||||
|
|
||||||
//QA를 제외한 경우에는 기본 QC로 이동한다
|
|
||||||
if (PUB.Result.NextPos == ePosition.QA)
|
|
||||||
PUB.Result.TargetPos = ePosition.QA;
|
|
||||||
else
|
|
||||||
PUB.Result.TargetPos = ePosition.QC;
|
|
||||||
|
|
||||||
|
|
||||||
PUB.sm.SetNewRunStep(ERunStep.GODOWN); //하차작업으로 전환
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ERunStep.GODOWN: //하차이동
|
|
||||||
if (_SM_RUN_GODOWN(runStepisFirst, PUB.sm.GetRunSteptime))
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.하차작업이완료되었습니다);
|
|
||||||
VAR.TIME.Update(eVarTime.ChargeTry);
|
|
||||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);// (Device.PLC.ZMotDirection.Down); //하차작업이 완료되면 커버를 내려서 바로 작업할 수 있게 한다.
|
|
||||||
|
|
||||||
//230601
|
|
||||||
//if (PUB.Result != null && PUB.sm != null)
|
|
||||||
// EEMStatus.AddEEDBSQL(PUB.sm.Step, PUB.sm.RunStep.ToString(), PUB.Result.TargetPos.ToString());
|
|
||||||
|
|
||||||
//하차가 완료되면 충전대기시간을 30초 남겨두고 없데이트한다
|
|
||||||
//충전이 필요할 경우 바로 될수있도록 220118
|
|
||||||
VAR.TIME[eVarTime.ChargeTry] = DateTime.Now.AddSeconds(-1 * PUB.setting.ChargeRetryTerm + 30);
|
|
||||||
|
|
||||||
if (PUB.Result.CurrentPos == ePosition.QC)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.대기상태로전환합니다);
|
|
||||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
PUB.log.AddAT("충전 해제로 대기상태로 전환 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ERunStep.LOADER_OUT: //로더아웃
|
||||||
|
if (_SM_RUN_LOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹해제완료);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.LoaderInComplete = false;
|
||||||
|
PUB.XBE.LoaderOutComplete = true;
|
||||||
|
|
||||||
|
//대기상태로 전환
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERunStep.LOADER_IN: //로더도킹
|
||||||
|
if (_SM_RUN_LOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹이완료되었습니다);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.LoaderInComplete = true;
|
||||||
|
|
||||||
|
//로더아웃으로 자동 진행하지 않음 (ACS 명령 대기)
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Command consumed
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//홈 이동 명령처리
|
// Legacy behavior or Goto command: Auto-exit?
|
||||||
|
// User said separation is key. Let's Stop here too or keep legacy for GOTO?
|
||||||
|
// Assuming GOTO might rely on this, but safer to STOP if we want strict separation.
|
||||||
|
// However, let's keep legacy behavior for GOTO if possible, but for PickOn/Off we STOP.
|
||||||
PUB.sm.ClearRunStep();
|
PUB.sm.ClearRunStep();
|
||||||
PUB.Result.TargetPos = ePosition.QC;
|
PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT);
|
||||||
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
|
|
||||||
PUB.Speak(Lang.홈위치로이동합니다);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ERunStep.GOHOME:
|
case ERunStep.UNLOADER_OUT: //언로더아웃
|
||||||
if (runStepisFirst)
|
if (_SM_RUN_UNLOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.홈검색을시작합니다);
|
PUB.Speak(Lang.버퍼도킹해제완료);
|
||||||
}
|
|
||||||
else if (_SM_RUN_GOHOME(runStepisFirst, PUB.sm.GetRunSteptime))
|
|
||||||
{
|
|
||||||
//230601
|
|
||||||
// if (PUB.Result != null && PUB.sm != null)
|
|
||||||
// EEMStatus.AddEEDBSQL(PUB.sm.Step, PUB.sm.RunStep.ToString(), PUB.Result.TargetPos.ToString());
|
|
||||||
|
|
||||||
PUB.Speak(Lang.홈이동완료대기상태로전환합니다);
|
//도킹완료상태를 업데이트한다.
|
||||||
VAR.TIME.Update(eVarTime.ChargeTry);
|
PUB.XBE.UnloaderInComplete = false;
|
||||||
PUB.sm.SetNewRunStep(ERunStep.READY); //대기상태로 전환한다
|
PUB.XBE.UnloaderOutComplete = true;
|
||||||
|
|
||||||
|
//대기상태로 전환
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
|
case ERunStep.UNLOADER_IN: //언로더도킹
|
||||||
|
if (_SM_RUN_UNLOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹이완료되었습니다);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.UnloaderInComplete = true;
|
||||||
|
|
||||||
}
|
//언로더아웃으로 자동 진행하지 않음
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
bool CheckStopCondition()
|
|
||||||
{
|
{
|
||||||
return true;
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
}
|
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||||
|
|
||||||
|
|
||||||
void CheckAGVMoveTo(eGoDir dir)
|
|
||||||
{
|
|
||||||
//계속내려간다
|
|
||||||
if (dir == eGoDir.Down)
|
|
||||||
{
|
|
||||||
var tsCmd = DateTime.Now - LastCommandTime;
|
|
||||||
if (tsCmd.TotalMilliseconds >= 1999)
|
|
||||||
{
|
|
||||||
//현재 동작중인상태에서 방향이 맞지 않다면 일단 움직임을 멈춘다
|
|
||||||
if (PUB.AGV.system1.agv_run)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.data.Direction == 'B')
|
|
||||||
{
|
|
||||||
PUB.log.Add($"방향이 맞지 않아 정지 합니다({dir})");
|
|
||||||
PUB.AGV.AGVMoveStop("CheckAGVMoveTo");
|
|
||||||
}
|
|
||||||
else if (PUB.AGV.data.Speed == 'S')
|
|
||||||
{
|
|
||||||
PUB.log.Add($"마크정지를 해제하기 위해 장비를 멈춥니다");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//움직이지 않으므로 전진하도록 한다
|
PUB.sm.ClearRunStep();
|
||||||
PUB.log.Add($"AGV 기동 방향(DOWN):{dir}");
|
PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT);
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
|
||||||
}
|
}
|
||||||
LastCommandTime = DateTime.Now;
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERunStep.CLEANER_OUT: //클리너아웃
|
||||||
|
if (_SM_RUN_CLEANER_OUT(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹해제완료);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.CleanerInComplete = false;
|
||||||
|
PUB.XBE.CleanerOutComplete = true;
|
||||||
|
|
||||||
|
//대기상태로 전환
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERunStep.CLEANER_IN: //클리너도킹
|
||||||
|
if (_SM_RUN_CLEANER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹이완료되었습니다);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.CleanerInComplete = true;
|
||||||
|
|
||||||
|
//클리너아웃으로 자동 진행하지 않음
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var tsCmd = DateTime.Now - LastCommandTime;
|
PUB.sm.ClearRunStep();
|
||||||
if (tsCmd.TotalMilliseconds >= 1999)
|
PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT);
|
||||||
{
|
|
||||||
if (PUB.AGV.system1.agv_run)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.data.Direction == 'F')
|
|
||||||
{
|
|
||||||
PUB.log.Add($"방향이 맞지 않아 정지 합니다({dir})");
|
|
||||||
PUB.AGV.AGVMoveStop("CheckAGVMoveTo");
|
|
||||||
}
|
}
|
||||||
else if (PUB.AGV.data.Speed == 'S')
|
return;
|
||||||
{
|
|
||||||
PUB.log.Add($"마크정지를 해제하기 위해 장비를 멈춥니다");
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERunStep.BUFFER_OUT: //버퍼아웃
|
||||||
|
if (_SM_RUN_BUFFER_OUT(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹해제완료);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.BufferInComplete = false;
|
||||||
|
PUB.XBE.BufferOutComplete = true;
|
||||||
|
|
||||||
|
//대기상태로 전환
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERunStep.BUFFER_IN: //버퍼도킹
|
||||||
|
if (_SM_RUN_BUFFER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
|
||||||
|
{
|
||||||
|
PUB.Speak(Lang.버퍼도킹이완료되었습니다);
|
||||||
|
|
||||||
|
//도킹완료상태를 업데이트한다.
|
||||||
|
PUB.XBE.BufferInComplete = true;
|
||||||
|
|
||||||
|
//버퍼아웃으로 자동 진행하지 않음
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PUB.log.Add($"AGV 기동 방향(UP):{dir}");
|
PUB.sm.ClearRunStep();
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT);
|
||||||
}
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckAGVStopbyMARK(string sender)
|
|
||||||
{
|
|
||||||
//계속내려간다
|
|
||||||
var tsCmd = DateTime.Now - LastCommandTime;
|
|
||||||
if (VAR.BOOL[eVarBool.NEXTSTOP_MARK] == false || tsCmd.TotalMilliseconds >= 1500)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("CheckAGVStopbyMARK", arDev.Narumi.eStopOpt.MarkStop);
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
PUB.log.Add($"[{sender}] MARK신호에 멈춤 설정");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Boolean UpdateMotionPositionForMark(string sender)
|
|
||||||
{
|
|
||||||
//이머전시상태에서는 처리하지 않는다.
|
|
||||||
if (VAR.BOOL[eVarBool.EMERGENCY]) return false;
|
|
||||||
|
|
||||||
//DOWN 작업
|
|
||||||
// if (goDIR == eGoDir.Down)
|
|
||||||
{
|
|
||||||
//1. 현재위치 > 대상위치
|
|
||||||
if (PUB.Result.CurrentPos > PUB.Result.TargetPos)
|
|
||||||
{
|
|
||||||
//계속내려간다
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
else
|
|
||||||
CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
}
|
|
||||||
//2. 현재위치 < 대상위치
|
|
||||||
else if (PUB.Result.CurrentPos < PUB.Result.TargetPos)
|
|
||||||
{
|
|
||||||
//올라가야한다
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
else
|
|
||||||
CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
}
|
|
||||||
//3. 현재위치 = 대상위치
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//현재위치가 확정되었는가?
|
|
||||||
var actpos = ctlPos1.GetPositionActive(PUB.Result.CurrentPos);
|
|
||||||
if (actpos == false && PUB.AGV.system1.agv_stop == true)
|
|
||||||
{
|
|
||||||
//위치확정이되지 않았다면 AGV멈춤시에 기동하게 한다.
|
|
||||||
var lastcom = DateTime.Now - LastCommandTime;
|
|
||||||
if (lastcom.TotalSeconds > 3)
|
|
||||||
{
|
|
||||||
if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
{
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
|
||||||
else
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
|
||||||
else
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
PUB.logagv.Add($"AGV가 멈춰있다 동작을 재개 합니다");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//마크센서가 들어와잇고, 위치가 act 되어있다면 해당 위치에 있는 것이다
|
|
||||||
if (PUB.AGV.error.Emergency == false &&
|
|
||||||
PUB.AGV.system1.agv_stop &&
|
|
||||||
PUB.AGV.signal.mark_sensor && actpos &&
|
|
||||||
PUB.Result.CurrentPos == PUB.Result.TargetPos)
|
|
||||||
{
|
|
||||||
//PUB.AGV.AGVMoveStop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PUB.AGV.system1.agv_stop == true && PUB.AGV.system1.agv_run == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add($"멈춰있으므로 이동을 시작 합니다");
|
|
||||||
if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
{
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
|
||||||
else
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (PUB.setting.AGV_Direction_FVI_Backward)
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
|
||||||
else
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//AGV는 아래로 FVI방향으로 내려가고 있다
|
|
||||||
if (VAR.BOOL[eVarBool.AGVDIR_UP] == false)
|
|
||||||
{
|
|
||||||
if (PUB.Result.CurrentPosCW == "0")
|
|
||||||
{
|
|
||||||
//장비가 마크센서에의해 멈췃다면 완료이다
|
|
||||||
if (PUB.AGV.error.Emergency == false && PUB.AGV.signal.mark_sensor)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("UPdateMotionPositionForMark");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else CheckAGVStopbyMARK(sender);
|
|
||||||
}
|
|
||||||
else if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
{
|
|
||||||
//내려가는 작업이고 AGV는 올라가고 있는데 RFID는 위졲이 감지되었다면 아래로 내려가야한다
|
|
||||||
CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PUB.Result.CurrentPosCW = "1";
|
|
||||||
CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//AGV는 Qc방향으로 올라가고 있다
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
{
|
|
||||||
//네려가는 방향에서 내려가는 위치가 인식되었다면 마크에서 멈춰야 한다
|
|
||||||
if (PUB.AGV.error.Emergency == false && PUB.AGV.signal.mark_sensor)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("UPdateMotionPositionForMark");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else CheckAGVStopbyMARK(sender);
|
|
||||||
}
|
|
||||||
else if (PUB.Result.CurrentPosCW == "0")
|
|
||||||
{
|
|
||||||
//내려가는 작업이고 AGV는 올라가고 있는데 RFID는 위졲이 감지되었다면 아래로 내려가야한다
|
|
||||||
CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PUB.Result.CurrentPosCW = "0";
|
|
||||||
CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//UP 작업
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// //1. 현재위치 > 대상위치
|
|
||||||
// if (PUB.Result.CurrentPos > PUB.Result.TargetPos)
|
|
||||||
// {
|
|
||||||
// //계속내려간다
|
|
||||||
// CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
// }
|
|
||||||
// //2. 현재위치 < 대상위치
|
|
||||||
// else if (PUB.Result.CurrentPos < PUB.Result.TargetPos)
|
|
||||||
// {
|
|
||||||
// //올라가야한다
|
|
||||||
// CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
// }
|
|
||||||
// //3. 현재위치 = 대상위치
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// //AGV는 위로가고 있다
|
|
||||||
// if (VAR.BOOL[eVarBool.AGVDIR_UP] == true)
|
|
||||||
// {
|
|
||||||
// if (PUB.Result.CurrentPosCW == "0")
|
|
||||||
// {
|
|
||||||
// //장비가 마크센서에의해 멈췃다면 완료이다
|
|
||||||
// if (PUB.AGV.system1.agv_stop && PUB.AGV.system1.stop_by_front_detect == false && PUB.AGV.error.Emergency == false && PUB.AGV.signal.mark_sensor)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// else CheckAGVStopbyMARK();
|
|
||||||
// }
|
|
||||||
// else if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
// {
|
|
||||||
// //내려가는 작업이고 AGV는 올라가고 있는데 RFID는 위졲이 감지되었다면 아래로 내려가야한다
|
|
||||||
// CheckAGVMoveTo(eGoDir.Down);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// //AGV는 내려가고 있다
|
|
||||||
// else if (VAR.BOOL[eVarBool.AGVDIR_UP] == false)
|
|
||||||
// {
|
|
||||||
// if (PUB.Result.CurrentPosCW == "1")
|
|
||||||
// {
|
|
||||||
// //네려가는 방향에서 내려가는 위치가 인식되었다면 마크에서 멈춰야 한다
|
|
||||||
// if (PUB.AGV.system1.agv_stop && PUB.AGV.system1.stop_by_front_detect == false && PUB.AGV.error.Emergency == false && PUB.AGV.signal.mark_sensor)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// else CheckAGVStopbyMARK();
|
|
||||||
// }
|
|
||||||
// else if (PUB.Result.CurrentPosCW == "0")
|
|
||||||
// {
|
|
||||||
// //내려가는 작업이고 AGV는 올라가고 있는데 RFID는 위졲이 감지되었다면 아래로 내려가야한다
|
|
||||||
// CheckAGVMoveTo(eGoDir.Up);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
Boolean UpdateMotionPositionForCharger(string sender)
|
|
||||||
{
|
|
||||||
if (VAR.BOOL[eVarBool.AGVDIR_UP] == false)// PUB.flag.get(EFlag.FLAG_DIR_BW) == true)
|
|
||||||
{
|
|
||||||
//충전기 검색은 항상 뒤로 검색한다
|
|
||||||
var tsCmd = DateTime.Now - tm_gocharge_command;
|
|
||||||
if (tsCmd.TotalMilliseconds >= 1999 &&
|
|
||||||
PUB.AGV.error.Emergency == false &&
|
|
||||||
PUB.AGV.system1.agv_run == false)
|
|
||||||
{
|
|
||||||
//PUB.PLC.Move(Device.PLC.Rundirection.Backward, "UpdateMotionPosition(" + sender + ")");
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);//
|
|
||||||
tm_gocharge_command = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//CCW (역방향 )
|
|
||||||
//if (Pub.Result.RFIDPos < ePosition.CHARGE || Pub.Result.RFIDPos > ePosition.QC)
|
|
||||||
//{
|
|
||||||
// //정상적이라면 RFID값은 QC + 혹은 QC - 에 있어야 하므로 항상 차저보다 높이 있다
|
|
||||||
// //그렇지 않다면 초기화해서 QC검색부터 다시 실행하게 한다
|
|
||||||
// //여기는 비정상 위치 값이다.
|
|
||||||
// Pub.sm.SetStepSeq(1);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
//현재위치가 충전위치이고, 움직이지 않았다면 완료된 경우라 할수 있따
|
|
||||||
if (PUB.Result.CurrentPos == ePosition.CHARGE &&
|
|
||||||
PUB.AGV.signal.mark_sensor == true)
|
|
||||||
{
|
|
||||||
PUB.log.AddI("충전위치 검색 완료");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//이동중이지 않다면 항상 이동을 해줘야한다
|
|
||||||
var tsCmd = DateTime.Now - LastCommandTime;
|
|
||||||
if (tsCmd.TotalMilliseconds >= 1999 &&
|
|
||||||
PUB.AGV.error.Emergency == false &&
|
|
||||||
PUB.AGV.system1.agv_run == false)
|
|
||||||
{
|
|
||||||
//PUB.PLC.Move(Device.PLC.Rundirection.Backward, "UpdateMotionPosition #1(" + sender + ")");
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);//
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}//cvass
|
}//cvass
|
||||||
}
|
}
|
||||||
|
|||||||
268
Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs
Normal file
268
Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Project.StateMachine;
|
||||||
|
using COMM;
|
||||||
|
using AR;
|
||||||
|
|
||||||
|
namespace Project
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 버퍼도킹
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isFirst"></param>
|
||||||
|
/// <param name="stepTime"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Boolean _SM_RUN_BUFFER_IN(bool isFirst, TimeSpan stepTime)
|
||||||
|
{
|
||||||
|
var funcname = "_SM_RUN_BUFFER_IN";
|
||||||
|
var idx = 1;
|
||||||
|
|
||||||
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
//라이더멈춤이 설정되어있다면 음성으로 알려준다
|
||||||
|
if (CheckLiderStop() == false) return false;
|
||||||
|
|
||||||
|
//현재 위치가 결정되어있는지 체크한다
|
||||||
|
if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 버퍼IN시퀀스
|
||||||
|
* 1. 회전이 진행되지 않았다면 회전을 진행한다.
|
||||||
|
* 2. LIFT DOWN
|
||||||
|
* 3. 후진-저속-마크다운 실행
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
PUB.log.Add("버퍼도킹시작");
|
||||||
|
PUB.Speak(Lang.하차작업을시작합니다);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//모션 전후진 제어
|
||||||
|
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90)
|
||||||
|
{
|
||||||
|
//동작중이면 동작을 멈춘다
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastStopCommandTime);
|
||||||
|
if (ts.TotalSeconds > 3)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
VAR.TIME.Update(eVarTime.LastStopCommandTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else PUB.sm.UpdateRunStepSeq(); //agv가 멈춰있으므로 턴을 진행해야 한다
|
||||||
|
}
|
||||||
|
else PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90)
|
||||||
|
{
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.AGV.AGVMoveLeft180Turn();
|
||||||
|
PUB.log.Add("AGV Left Turn");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
}
|
||||||
|
else PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90)
|
||||||
|
{
|
||||||
|
//5초이내에 턴이동 상태가 확인되어야 한다.
|
||||||
|
if (PUB.AGV.system1.agv_run == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 5)
|
||||||
|
{
|
||||||
|
//5초동안 AGV까 움직이지 않았다면 오류 처리한다.
|
||||||
|
PUB.AGV.AGVMoveStop("5초이내 턴 감지 안됨");
|
||||||
|
PUB.log.AddE("5초이내 턴 감지 안됨");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//턴이완료되었느닞 확인한다.
|
||||||
|
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90)
|
||||||
|
{
|
||||||
|
//10초 이상 가동하고 있다면 문제이다
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 15)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop("15초이내 턴 이 완료되지 않음");
|
||||||
|
PUB.log.AddE("5초이내 턴 완료 확인 안됨");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.L90;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// [PickOn/PickOff] 초기 리프트 동작
|
||||||
|
var liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AGV.LiftControl(liftCmd);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//리프트 센서 확인
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
// Timebound check
|
||||||
|
}
|
||||||
|
PUB.log.Add("리프트 동작 확인 완료");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑셋팅
|
||||||
|
PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//저속이동 (후진 진입)
|
||||||
|
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||||
|
{
|
||||||
|
Bunki = arDev.Narumi.eBunki.Strate,
|
||||||
|
Direction = arDev.Narumi.eMoveDir.Backward,
|
||||||
|
PBSSensor = 0,
|
||||||
|
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||||
|
});
|
||||||
|
if (moveset == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("AGV속도설정이 완료되지 않았습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
||||||
|
PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑신호가 3초이내로 들어와야 한다
|
||||||
|
if (VAR.BOOL[eVarBool.NEXTSTOP_MARK] == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 3)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("MARK STOP신호가 확인되지 않습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//AGV가 멈출때까지 기다린다.
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("AGV가 멈추지 않아 강제종료 합니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||||
|
PUB.log.Add("버퍼 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||||
|
|
||||||
|
var liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AGV.LiftControl(liftCmd);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// 리프트 동작 대기
|
||||||
|
// TODO: 실제 센서 확인 로직 추가 필요
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds < 2) return false;
|
||||||
|
|
||||||
|
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환 (퇴출 명령 대기)");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//완료되었다. (ACS에 보내야함)
|
||||||
|
PUB.log.Add("버퍼 진입 및 작업 완료");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 작업을 마치고 설비 안에 멈춰있는 상태.
|
||||||
|
// ACS가 이 상태를 확인하고 NextWorkCmd로 퇴출(Out) 명령을 보내야 함.
|
||||||
|
PUB.AddEEDB($"버퍼작업완료({PUB.Result.TargetPos})");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs
Normal file
113
Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Project.StateMachine;
|
||||||
|
using COMM;
|
||||||
|
using AR;
|
||||||
|
|
||||||
|
namespace Project
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
|
||||||
|
public Boolean _SM_RUN_BUFFER_OUT(bool isFirst, TimeSpan stepTime)
|
||||||
|
{
|
||||||
|
|
||||||
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
//라이더멈춤이 설정되어있다면 음성으로 알려준다
|
||||||
|
if (CheckLiderStop() == false) return false;
|
||||||
|
|
||||||
|
//현재 위치가 결정되어있는지 체크한다
|
||||||
|
if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
var idx = 1;
|
||||||
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//빈 상태로 아웃해야한다.
|
||||||
|
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||||
|
{
|
||||||
|
Bunki = arDev.Narumi.eBunki.Strate,
|
||||||
|
Direction = arDev.Narumi.eMoveDir.Forward,
|
||||||
|
PBSSensor = 0,
|
||||||
|
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||||
|
});
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//전진이동
|
||||||
|
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑
|
||||||
|
PUB.AGV.AGVMoveStop("buffer out", arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//이동확인
|
||||||
|
if (PUB.AGV.system1.agv_run == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//멈춤확인
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//우측으로 180도 턴
|
||||||
|
PUB.AGV.AGVMoveRight180Turn();
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//이동확인
|
||||||
|
if (PUB.AGV.system1.agv_run == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//멈춤확인
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
PUB.log.Add("BufferOut Complete");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AddEEDB($"bufferout 완료({PUB.Result.TargetPos})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace Project
|
|||||||
|
|
||||||
public Boolean _SM_RUN_CHGOFF(bool isFirst, TimeSpan stepTime)
|
public Boolean _SM_RUN_CHGOFF(bool isFirst, TimeSpan stepTime)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//충전중인지 확인한다.
|
//충전중인지 확인한다.
|
||||||
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
|
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
|
||||||
{
|
{
|
||||||
@@ -47,6 +49,17 @@ namespace Project
|
|||||||
PUB.AGV.AGVCharge(PUB.setting.ChargerID, false);
|
PUB.AGV.AGVCharge(PUB.setting.ChargerID, false);
|
||||||
VAR.TIME.Update(eVarTime.SendChargeOff);
|
VAR.TIME.Update(eVarTime.SendChargeOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 1분 타임아웃 체크
|
||||||
|
if (stepTime.TotalMinutes >= 1)
|
||||||
|
{
|
||||||
|
PUB.XBE.ErrorMessage = $"충전해제가 실패되었습니다(1분)";
|
||||||
|
PUB.log.AddE(PUB.XBE.ErrorMessage);
|
||||||
|
PUB.sm.SetNewStep(eSMStep.IDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
180
Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs
Normal file
180
Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Project.StateMachine;
|
||||||
|
using COMM;
|
||||||
|
using AR;
|
||||||
|
|
||||||
|
namespace Project
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 클리너 진입
|
||||||
|
/// </summary>
|
||||||
|
public Boolean _SM_RUN_CLEANER_IN(bool isFirst, TimeSpan stepTime)
|
||||||
|
{
|
||||||
|
var funcname = "_SM_RUN_CLEANER_IN";
|
||||||
|
var idx = 1;
|
||||||
|
|
||||||
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
//라이더멈춤이 설정되어있다면 음성으로 알려준다
|
||||||
|
if (CheckLiderStop() == false) return false;
|
||||||
|
|
||||||
|
//현재 위치가 결정되어있는지 체크한다
|
||||||
|
if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 클리너 IN 시퀀스 (버퍼 복사 - 턴 제거)
|
||||||
|
* 1. LIFT DOWN
|
||||||
|
* 2. 후진-저속-마크다운 실행
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
PUB.log.Add("클리너진입시작");
|
||||||
|
PUB.Speak("클리너 작업을 시작합니다");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// [PickOn/PickOff] 초기 리프트 동작
|
||||||
|
var liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AGV.LiftControl(liftCmd);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//리프트 센서 확인
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
// Timebound check
|
||||||
|
}
|
||||||
|
PUB.log.Add("리프트 동작 확인 완료");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑셋팅
|
||||||
|
PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//저속이동 (후진 진입)
|
||||||
|
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||||
|
{
|
||||||
|
Bunki = arDev.Narumi.eBunki.Strate,
|
||||||
|
Direction = arDev.Narumi.eMoveDir.Backward,
|
||||||
|
PBSSensor = 0,
|
||||||
|
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||||
|
});
|
||||||
|
if (moveset == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("AGV속도설정이 완료되지 않았습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
||||||
|
PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑신호가 3초이내로 들어와야 한다
|
||||||
|
if (VAR.BOOL[eVarBool.NEXTSTOP_MARK] == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 3)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("MARK STOP신호가 확인되지 않습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//AGV가 멈출때까지 기다린다.
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds > 10)
|
||||||
|
{
|
||||||
|
PUB.AGV.AGVMoveStop(funcname);
|
||||||
|
PUB.log.AddE("AGV가 멈추지 않아 강제종료 합니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||||
|
PUB.log.Add("클리너 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||||
|
|
||||||
|
var liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||||
|
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||||
|
{
|
||||||
|
liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AGV.LiftControl(liftCmd);
|
||||||
|
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
// 리프트 동작 대기
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||||
|
if (ts.TotalSeconds < 2) return false;
|
||||||
|
|
||||||
|
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//완료되었다.
|
||||||
|
PUB.log.Add("클리너 진입 및 작업 완료");
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PUB.AddEEDB($"클리너작업완료({PUB.Result.TargetPos})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs
Normal file
80
Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Project.StateMachine;
|
||||||
|
using COMM;
|
||||||
|
using AR;
|
||||||
|
|
||||||
|
namespace Project
|
||||||
|
{
|
||||||
|
public partial class fMain
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 클리너 배출
|
||||||
|
/// </summary>
|
||||||
|
public Boolean _SM_RUN_CLEANER_OUT(bool isFirst, TimeSpan stepTime)
|
||||||
|
{
|
||||||
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
//라이더멈춤이 설정되어있다면 음성으로 알려준다
|
||||||
|
if (CheckLiderStop() == false) return false;
|
||||||
|
|
||||||
|
//현재 위치가 결정되어있는지 체크한다
|
||||||
|
if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false;
|
||||||
|
|
||||||
|
var idx = 1;
|
||||||
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//빈 상태로 아웃해야한다.
|
||||||
|
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||||
|
{
|
||||||
|
Bunki = arDev.Narumi.eBunki.Strate,
|
||||||
|
Direction = arDev.Narumi.eMoveDir.Forward,
|
||||||
|
PBSSensor = 0,
|
||||||
|
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||||
|
});
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//전진이동
|
||||||
|
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//마크스탑
|
||||||
|
PUB.AGV.AGVMoveStop("cleaner out", arDev.Narumi.eStopOpt.MarkStop);
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//이동확인
|
||||||
|
if (PUB.AGV.system1.agv_run == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
|
{
|
||||||
|
//멈춤확인
|
||||||
|
if (PUB.AGV.system1.agv_run == true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB.sm.UpdateRunStepSeq();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,17 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//이미 충전중이라면 바로 완료 처리한다 (사용자 요청)
|
||||||
|
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
|
||||||
|
{
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
PUB.log.Add("이미 충전 중이므로 충전 시퀀스를 종료하고 준비 상태로 전환합니다.");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//충전 상태가 OFF되어야 동작하게한다
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
||||||
return false;
|
return false;
|
||||||
@@ -38,7 +49,7 @@ namespace Project
|
|||||||
if (PUB.AGV.system1.stop_by_front_detect == true)
|
if (PUB.AGV.system1.stop_by_front_detect == true)
|
||||||
{
|
{
|
||||||
var tsSpeak = DateTime.Now - LastSpeakTime;
|
var tsSpeak = DateTime.Now - LastSpeakTime;
|
||||||
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
|
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
||||||
LastSpeakTime = DateTime.Now;
|
LastSpeakTime = DateTime.Now;
|
||||||
@@ -63,19 +74,27 @@ namespace Project
|
|||||||
var idx = 1;
|
var idx = 1;
|
||||||
if (PUB.sm.RunStepSeq == idx++)
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.충전을위해홈위치로이동합니다);
|
var targetnode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Charger);
|
||||||
PUB.Result.TargetPos = ePosition.QC;
|
if (targetnode == null)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"충전기 노드가 설정되지 않았습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//충전기로 대상을 설정한다
|
||||||
|
PUB._virtualAGV.TargetNode = targetnode;
|
||||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
PUB.log.Add($"충전:대상위치 QC 시작");
|
PUB.log.Add($"충전기 위치로 이동 목표:{targetnode.RfidId}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
{
|
||||||
//모션 전후진 제어
|
//모션 전후진 제어
|
||||||
if (UpdateMotionPositionForMark("GOCHARGE #1") == true)
|
if (UpdateMotionPositionForCharger("GOCHARGE #1") == true)
|
||||||
{
|
{
|
||||||
PUB.log.Add($"충전:충전기 검색 전 QC위치 확인 완료");
|
PUB.log.Add($"충전기 목표위치 이동 완료");
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -97,8 +116,6 @@ namespace Project
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
|
||||||
if (PUB.setting.chargerpos == 0) //down search
|
|
||||||
{
|
{
|
||||||
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
||||||
PUB.Speak(Lang.충전기를검색합니다);
|
PUB.Speak(Lang.충전기를검색합니다);
|
||||||
@@ -112,33 +129,11 @@ namespace Project
|
|||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
||||||
//PUB.Result.TargetPos = ePosition.CHARGE;
|
//PUB.Result.TargetPos = ePosition.CHARGE;
|
||||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||||
}
|
|
||||||
else if (PUB.setting.chargerpos == 2) //up search
|
|
||||||
{
|
|
||||||
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
|
||||||
PUB.Speak(Lang.충전기를검색합니다);
|
|
||||||
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
|
||||||
{
|
|
||||||
Speed = arDev.Narumi.eMoveSpd.Low,
|
|
||||||
Bunki = arDev.Narumi.eBunki.Strate,
|
|
||||||
Direction = arDev.Narumi.eMoveDir.Backward,
|
|
||||||
PBSSensor = 1,
|
|
||||||
});
|
|
||||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
|
||||||
//PUB.Result.TargetPos = ePosition.CHARGE;
|
|
||||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PUB.log.Add($"충전기위치가 QC위치로 설정되어 있습니다");
|
|
||||||
}
|
|
||||||
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
|
||||||
if (PUB.setting.chargerpos != 1)
|
|
||||||
{
|
{
|
||||||
if (PUB.AGV.system1.agv_run)
|
if (PUB.AGV.system1.agv_run)
|
||||||
{
|
{
|
||||||
@@ -157,13 +152,6 @@ namespace Project
|
|||||||
PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함");
|
PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -200,9 +188,6 @@ namespace Project
|
|||||||
PUB.Result.CurrentPos = ePosition.CHARGE; //현재위치를 충전기로 한다
|
PUB.Result.CurrentPos = ePosition.CHARGE; //현재위치를 충전기로 한다
|
||||||
PUB.Result.TargetPos = ePosition.CHARGE;
|
PUB.Result.TargetPos = ePosition.CHARGE;
|
||||||
PUB.Result.CurrentPosCW = "1";
|
PUB.Result.CurrentPosCW = "1";
|
||||||
ctlPos1.SetPosition(ePosition.CHARGE);
|
|
||||||
ctlPos1.SetPositionActive(ePosition.CHARGE);
|
|
||||||
ctlPos1.Invalidate();
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -254,7 +239,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Project.StateMachine;
|
|
||||||
using COMM;
|
|
||||||
using AR;
|
|
||||||
|
|
||||||
namespace Project
|
|
||||||
{
|
|
||||||
public partial class fMain
|
|
||||||
{
|
|
||||||
public Boolean _SM_RUN_GODOWN(bool isFirst, TimeSpan stepTime)
|
|
||||||
{
|
|
||||||
if (runStepisFirst)
|
|
||||||
{
|
|
||||||
//PUB.flag.set(EFlag.FLAG_NEXTSTOP_ALIGN, false);
|
|
||||||
VAR.BOOL[eVarBool.FLAG_NEXTSTOP_ALIGN] = false;//
|
|
||||||
//VAR.BOOL[eVarBool.FLAG_NEXTSTOP_MARK] = false;//);
|
|
||||||
}
|
|
||||||
|
|
||||||
//HW 연결오류
|
|
||||||
if (PUB.AGV.IsOpen == false)
|
|
||||||
{
|
|
||||||
PUB.Result.SetResultMessage(eResult.Hardware, eECode.AGVCONN, eNextStep.ERROR);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//충전 상태가 OFF되어야 동작하게한다
|
|
||||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//라이더멈춤이 설정되어있다면 음성으로 알려준다 200409
|
|
||||||
if (PUB.AGV.system1.stop_by_front_detect == true)
|
|
||||||
{
|
|
||||||
var tsSpeak = DateTime.Now - LastSpeakTime;
|
|
||||||
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
|
||||||
LastSpeakTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//현재 위치가 결정되어있는지 체크한다
|
|
||||||
if (_SM_RUN_POSCHK(isFirst, stepTime) == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (PUB.sm.RunStepSeq == 1)
|
|
||||||
{
|
|
||||||
//하차는 무조건 대상이 QC가 된다
|
|
||||||
if (PUB.Result.TargetPos == ePosition.NONE)
|
|
||||||
PUB.Result.TargetPos = ePosition.QC;
|
|
||||||
|
|
||||||
//if (PUB.Result.TargetPos == ePosition.QA &&
|
|
||||||
// PUB.Result.CurrentPos == ePosition.QC &&
|
|
||||||
// (PUB.PLC.IsLimitDn()))
|
|
||||||
//{
|
|
||||||
// PUB.Speak("안전 커버를 올리고 다시 시도하세요", true);
|
|
||||||
// PUB.sm.ClearRunStep();
|
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
|
||||||
//}
|
|
||||||
PUB.AddEEDB($"하차작업시작({PUB.Result.TargetPos})");
|
|
||||||
PUB.Speak(Lang.하차작업을시작합니다);
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 2)
|
|
||||||
{
|
|
||||||
//모션 전후진 제어
|
|
||||||
if (UpdateMotionPositionForMark("GODOWN"))
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 3)
|
|
||||||
{
|
|
||||||
//위치 확정이 완료될때까지 대기
|
|
||||||
if (PUB.Result.CurrentPos == PUB.Result.TargetPos)
|
|
||||||
{
|
|
||||||
//PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GODOWN:위치확정");
|
|
||||||
PUB.AGV.AGVMoveStop("sm_run_godown");
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
else if (PUB.AGV.system1.agv_run == false)
|
|
||||||
{
|
|
||||||
//움직이지않으면 방향을 다시 조정한다
|
|
||||||
PUB.sm.SetStepSeq(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 4)
|
|
||||||
{
|
|
||||||
//대상까지 모두 완료되었다.(완전히 정차할때까지 기다린다)
|
|
||||||
if (PUB.AGV.system1.agv_run == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add("이동 정지 확인");
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 5)
|
|
||||||
{
|
|
||||||
//하차수량증가
|
|
||||||
if (PUB.Result.TargetPos == ePosition.QA)
|
|
||||||
PUB.counter.CountQA += 1;
|
|
||||||
else
|
|
||||||
PUB.counter.CountQC += 1;
|
|
||||||
PUB.counter.Save();
|
|
||||||
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 6)
|
|
||||||
{
|
|
||||||
//커버를 자동으로 내려준다
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
UpdateProgressStatus(stepTime.TotalSeconds, 5, Lang.안전커버를내립니다);
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_DOWN] = true;
|
|
||||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);//
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 7)
|
|
||||||
{
|
|
||||||
//커버 내림이 완료될때까지 기다린다
|
|
||||||
//if (PUB.PLC.IsLimitDn() == true)
|
|
||||||
//{
|
|
||||||
// VAR.BOOL[eVarBool.WAIT_COVER_DOWN] = false;
|
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
//경과시간이 10초가 지나면 5초마다 음성을 출력한다
|
|
||||||
var tsCover = DateTime.Now - CoverControlTime;
|
|
||||||
if (tsCover.TotalSeconds >= 7)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.안전커버를내려주세요);
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 8)
|
|
||||||
{
|
|
||||||
//IO업데이트 간격 전송
|
|
||||||
UpdateProgressStatus(stepTime.TotalSeconds, 5, Lang.안전커버를올려주세요);
|
|
||||||
//PUB.Speak(Lang.안전커버를올리면하차가완료됩니다);
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_UP] = true;
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == 9)
|
|
||||||
{
|
|
||||||
//커버 올림이 완료될때까지 기다린다
|
|
||||||
if (VAR.BOOL[eVarBool.FLAG_LIMITHIGH] == true)
|
|
||||||
{
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_UP] = false;
|
|
||||||
VAR.BOOL[eVarBool.ITEMON] = false;
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//경과시간이 10초가 지나면 5초마다 음성을 출력한다
|
|
||||||
var tsCover = DateTime.Now - CoverControlTime;
|
|
||||||
if (tsCover.TotalSeconds >= PUB.setting.doorSoundTerm)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.안전커버를올려주세요);
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
|
|
||||||
////한쪽이 올라가 있는 상태에..
|
|
||||||
//if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU) == true)
|
|
||||||
//{
|
|
||||||
// //모터는 올리는 방향일때에...
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_LDIR) == true)
|
|
||||||
// {
|
|
||||||
// //모터가 멈춰있을때에..
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_LRUN) == false)
|
|
||||||
// {
|
|
||||||
// //자동으로 올려준다 (센서가 간혹 인식이 안되어서 .대기하는 경우가 잇음)
|
|
||||||
|
|
||||||
// //왼쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU) == false)
|
|
||||||
// PUB.PLC.ZMot_Left(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU) == true)
|
|
||||||
//{
|
|
||||||
// //모터는 올리는 방향일때에...
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_RDIR) == true)
|
|
||||||
// {
|
|
||||||
// //모터가 멈춰있을때에..
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_RRUN) == false)
|
|
||||||
// {
|
|
||||||
// //자동으로 올려준다 (센서가 간혹 인식이 안되어서 .대기하는 경우가 잇음)
|
|
||||||
|
|
||||||
// //왼쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU) == false)
|
|
||||||
// PUB.PLC.ZMot_Right(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PUB.AddEEDB($"하차작업완료({PUB.Result.TargetPos})");
|
|
||||||
EEMStatus.AddStatusCount(1, PUB.Result.TargetPos.ToString()); //230620
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,20 +13,12 @@ namespace Project
|
|||||||
{
|
{
|
||||||
public Boolean _SM_RUN_GOHOME(bool isFirst, TimeSpan stepTime)
|
public Boolean _SM_RUN_GOHOME(bool isFirst, TimeSpan stepTime)
|
||||||
{
|
{
|
||||||
|
var funcName = "_SM_RUN_GOHOME";
|
||||||
if (runStepisFirst)
|
if (runStepisFirst)
|
||||||
{
|
{
|
||||||
// PUB.flag.set(EFlag.FLAG_NEXTSTOP_ALIGN, false);
|
|
||||||
|
|
||||||
//VAR.BOOL[eVarBool.FLAG_NEXTSTOP_MARK] = false;//);
|
|
||||||
VAR.BOOL[eVarBool.FLAG_NEXTSTOP_ALIGN] = false;//);
|
VAR.BOOL[eVarBool.FLAG_NEXTSTOP_ALIGN] = false;//);
|
||||||
}
|
}
|
||||||
|
|
||||||
//220629
|
|
||||||
// if(PUB.flag.get(EFlag.FLAG_GO_CHAGER_TEMP))
|
|
||||||
//{
|
|
||||||
// PUB.flag.set(EFlag.FLAG_GO_CHAGER_TEMP, false);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//HW 연결오류
|
//HW 연결오류
|
||||||
if (PUB.AGV.IsOpen == false)
|
if (PUB.AGV.IsOpen == false)
|
||||||
{
|
{
|
||||||
@@ -34,7 +26,6 @@ namespace Project
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//충전 상태가 OFF되어야 동작하게한다
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
||||||
return false;
|
return false;
|
||||||
@@ -43,7 +34,7 @@ namespace Project
|
|||||||
if (PUB.AGV.system1.stop_by_front_detect == true)
|
if (PUB.AGV.system1.stop_by_front_detect == true)
|
||||||
{
|
{
|
||||||
var tsSpeak = DateTime.Now - LastSpeakTime;
|
var tsSpeak = DateTime.Now - LastSpeakTime;
|
||||||
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
|
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
||||||
LastSpeakTime = DateTime.Now;
|
LastSpeakTime = DateTime.Now;
|
||||||
@@ -51,27 +42,29 @@ namespace Project
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//현재 위치가 결정되어있는지 체크한다
|
|
||||||
if (_SM_RUN_POSCHK(isFirst, stepTime) == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var idx = 1;
|
var idx = 1;
|
||||||
if (PUB.sm.RunStepSeq == idx++)
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
{
|
||||||
PUB.Speak(Lang.홈으로이동합니다);
|
PUB.Speak(Lang.홈으로이동합니다);
|
||||||
|
|
||||||
//홈은 무조건 QC위치로 간다
|
var homenode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Home);
|
||||||
PUB.AddEEDB($"홈검색시작({PUB.Result.TargetPos})");
|
if (homenode == null)
|
||||||
PUB.Result.TargetPos = ePosition.QC;
|
{
|
||||||
|
PUB.log.Add($"홈위치의 노드맵핑(환경설정)이 없습니다");
|
||||||
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PUB._virtualAGV.TargetNode = homenode;
|
||||||
|
PUB.AddEEDB($"홈검색시작({homenode.RfidId})");
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
{
|
{
|
||||||
//모션 전후진 제어
|
//모션 전후진 제어
|
||||||
if (UpdateMotionPositionForMark("GOHOME"))
|
if (UpdateMotionPositionForMark(funcName))
|
||||||
{
|
{
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOHOME");
|
PUB.AGV.AGVMoveStop(funcName);
|
||||||
PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace Project
|
|||||||
{
|
{
|
||||||
///명령어 재전송 간격(기본 2초)
|
///명령어 재전송 간격(기본 2초)
|
||||||
var CommandInterval = 2;
|
var CommandInterval = 2;
|
||||||
|
var funcName = "_SM_RUN_GOTO";
|
||||||
|
|
||||||
//충전 상태가 OFF되어야 동작하게한다
|
//충전 상태가 OFF되어야 동작하게한다
|
||||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
||||||
@@ -29,267 +30,48 @@ namespace Project
|
|||||||
VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
|
VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
//목적지가 설정되었는지 체크한다.
|
//라이더멈춤이 설정되어있다면 음성으로 알려준다 200409
|
||||||
//Z if (PUB.mapctl.Manager.agv.TargetRFID.IsEmpty)
|
if (PUB.AGV.system1.stop_by_front_detect == true)
|
||||||
// {
|
{
|
||||||
// //최대 5초간 설정여부를 확인하고
|
var tsSpeak = DateTime.Now - LastSpeakTime;
|
||||||
// if (VAR.TIME.RUN(eVarTime.CheckGotoTargetSet).TotalSeconds > 5)
|
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
|
||||||
// {
|
{
|
||||||
// //실패시에는 READY로 전환한다.
|
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
LastSpeakTime = DateTime.Now;
|
||||||
// PUB.Speak(Lang.목적지가없어대기상태로전환합니다);
|
}
|
||||||
// }
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
//var idx = 1;
|
var idx = 1;
|
||||||
//var BeforePredictIdx = -1;
|
if (PUB.sm.RunStepSeq == idx++)
|
||||||
//var predict = PUB.mapctl.Manager.PredictResult;
|
{
|
||||||
//if (PUB.sm.RunStepSeq == idx++)
|
if(PUB._virtualAGV.TargetNode == null)
|
||||||
//{
|
{
|
||||||
// PUB.Speak(Lang.위치로이동합니다);
|
PUB.log.Add($"대상노드가 없어 이동을할 수 없습니다");
|
||||||
// PUB.log.Add($"목적지 위치 이동시작({PUB.mapctl.Manager.agv.TargetRFID.Value})");
|
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||||
// VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
|
return false;
|
||||||
// VAR.TIME.Set(eVarTime.SendGotoCommand, DateTime.Now.AddDays(-1));
|
}
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
PUB.sm.UpdateRunStepSeq();
|
||||||
// return false;
|
return false;
|
||||||
//}
|
}
|
||||||
//else if (PUB.sm.RunStepSeq == idx++)
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
//{
|
{
|
||||||
// //멈춰야하는경우
|
//모션 전후진 제어
|
||||||
// if (predict.MoveState == AGVControl.AGVMoveState.Stop)
|
if (UpdateMotionPositionForMark(funcName))
|
||||||
// {
|
{
|
||||||
// if (PUB.AGV.system1.agv_run)
|
PUB.AGV.AGVMoveStop(funcName);
|
||||||
// {
|
PUB.sm.UpdateRunStepSeq();
|
||||||
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > 2)
|
}
|
||||||
// {
|
return false;
|
||||||
// PUB.Speak("AGV Stop");
|
}
|
||||||
// PUB.AGV.AGVMoveStop("Predict", arDev.Narumi.eStopOpt.Stop);
|
else if (PUB.sm.RunStepSeq == idx++)
|
||||||
// VAR.TIME.Update(eVarTime.SendGotoCommand);
|
{
|
||||||
// }
|
//QC까지 모두 완료되었다.(완전히 정차할때까지 기다린다)
|
||||||
// return false;
|
PUB.Speak(Lang.홈검색완료, true);
|
||||||
// }
|
PUB.AddEEDB($"홈검색완료({PUB.Result.TargetPos})");
|
||||||
// else
|
PUB.sm.UpdateRunStepSeq();
|
||||||
// {
|
return false;
|
||||||
// //완료되었거나 턴을진행해야한다
|
}
|
||||||
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.Arrived ||
|
|
||||||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
|
|
||||||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
|
|
||||||
// {
|
|
||||||
// GotoTurnStep = 0;
|
|
||||||
// GotoTurnSetTime = DateTime.Now.AddDays(-1);
|
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else //이동해야하는 경우
|
|
||||||
// {
|
|
||||||
// //속도와 방향이 불일치하는 경우 다시 설정한다 (속도: H,L,M,[S]
|
|
||||||
// AGVControl.AgvDir AGV_Direction = (AGVControl.AgvDir)PUB.AGV.data.Direction;
|
|
||||||
// AGVControl.AgvSpeed AGV_Speed = (AGVControl.AgvSpeed)PUB.AGV.data.Speed;
|
|
||||||
// AGVControl.AgvSts AGV_Sts = (AGVControl.AgvSts)PUB.AGV.data.Sts;
|
|
||||||
|
|
||||||
// //상태값이 바뀌었다면 전송을 해야한다
|
|
||||||
// if (predict.Direction != AGV_Direction || predict.MoveSpeed != AGV_Speed || predict.MoveDiv != AGV_Sts)
|
|
||||||
// {
|
|
||||||
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > CommandInterval)
|
|
||||||
// {
|
|
||||||
// arDev.Narumi.eBunki v_bunki = arDev.Narumi.eBunki.Strate;
|
|
||||||
// if (predict.MoveDiv == AGVControl.AgvSts.Straight) v_bunki = arDev.Narumi.eBunki.Strate;
|
|
||||||
// else if (predict.MoveDiv == AGVControl.AgvSts.Left) v_bunki = arDev.Narumi.eBunki.Left;
|
|
||||||
// else if (predict.MoveDiv == AGVControl.AgvSts.Right) v_bunki = arDev.Narumi.eBunki.Right;
|
|
||||||
|
|
||||||
// arDev.Narumi.eMoveDir v_dir = arDev.Narumi.eMoveDir.Backward;
|
|
||||||
// if (predict.Direction == AGVControl.AgvDir.Forward) v_dir = arDev.Narumi.eMoveDir.Forward;
|
|
||||||
// else if (predict.Direction == AGVControl.AgvDir.Backward) v_dir = arDev.Narumi.eMoveDir.Backward;
|
|
||||||
|
|
||||||
// arDev.Narumi.eMoveSpd v_spd = arDev.Narumi.eMoveSpd.Low;
|
|
||||||
// if (predict.MoveSpeed == AGVControl.AgvSpeed.Middle) v_spd = arDev.Narumi.eMoveSpd.Middle;
|
|
||||||
// else if (predict.MoveSpeed == AGVControl.AgvSpeed.High) v_spd = arDev.Narumi.eMoveSpd.High;
|
|
||||||
// else if (predict.MoveSpeed == AGVControl.AgvSpeed.Low) v_spd = arDev.Narumi.eMoveSpd.Low;
|
|
||||||
|
|
||||||
// //이동셋팅을 해준다
|
|
||||||
// PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
|
||||||
// {
|
|
||||||
// Bunki = v_bunki,
|
|
||||||
// Direction = v_dir,
|
|
||||||
// PBSSensor = 1,
|
|
||||||
// Speed = v_spd,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (predict.MoveSpeed == AGVControl.AgvSpeed.MarkStop)
|
|
||||||
// {
|
|
||||||
// PUB.AGV.AGVMoveStop("Predict", arDev.Narumi.eStopOpt.Stop);
|
|
||||||
// }
|
|
||||||
// VAR.TIME.Update(eVarTime.SendGotoCommand);
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //정지상태라면 이동 명령을 전달한다
|
|
||||||
// if (PUB.AGV.system1.agv_run == false)
|
|
||||||
// {
|
|
||||||
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > CommandInterval)
|
|
||||||
// {
|
|
||||||
// PUB.Speak("AGV Start");
|
|
||||||
|
|
||||||
// arDev.Narumi.eRunOpt v_dir = arDev.Narumi.eRunOpt.Backward;
|
|
||||||
// if (predict.Direction == AGVControl.AgvDir.Forward) v_dir = arDev.Narumi.eRunOpt.Forward;
|
|
||||||
// else if (predict.Direction == AGVControl.AgvDir.Backward) v_dir = arDev.Narumi.eRunOpt.Backward;
|
|
||||||
|
|
||||||
// PUB.AGV.AGVMoveRun(v_dir);
|
|
||||||
// VAR.TIME.Update(eVarTime.SendGotoCommand);
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //예측이 업데이트되지 않으면 오류 처리해야한다
|
|
||||||
// if (BeforePredictIdx == -1) BeforePredictIdx = (int)predict.Idx;
|
|
||||||
// else if (BeforePredictIdx != predict.Idx) //이전사용한 IDX와 다르다면 예측이 실행된 경우이다
|
|
||||||
// BeforePredictIdx = (int)predict.Idx;
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// //5초이상 예측값이 업데이트되지 않으면 오류 처리한다.
|
|
||||||
// var tsPredict = DateTime.Now - predict.CreateTime;
|
|
||||||
// if (tsPredict.TotalSeconds > 5)
|
|
||||||
// {
|
|
||||||
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.PredictFix, Lang.예측값이계산되지않아이동을중단합니다);
|
|
||||||
// PUB.Speak(Lang.예측값이계산되지않아이동을중단합니다);
|
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
//else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
//{
|
|
||||||
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.Arrived)
|
|
||||||
// {
|
|
||||||
// PUB.Speak(Lang.목적지이동이완료되었습니다);
|
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
|
||||||
// }
|
|
||||||
// else if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
|
|
||||||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
|
|
||||||
// {
|
|
||||||
// //턴을 해야하는 경우이다
|
|
||||||
// //좌턴을 기본으로 진행하며, 좌턴이동 후 마크스탑을 입력한다
|
|
||||||
// if (GotoTurnStep == 0)
|
|
||||||
// {
|
|
||||||
// //턴을 한적이 없으므로 턴을 먼저 진행한다
|
|
||||||
// arDev.Narumi.eMoveDir moveDir = arDev.Narumi.eMoveDir.Backward;
|
|
||||||
// if (predict.Direction == AGVControl.AgvDir.Forward) moveDir = arDev.Narumi.eMoveDir.Forward;
|
|
||||||
// if (PUB.AGV.data.Sts != 'L' || PUB.AGV.data.Speed != 'L' || PUB.AGV.data.Direction != moveDir.ToString()[0])
|
|
||||||
// {
|
|
||||||
// //셋팅이 다르다면 3초간격으로 전송한다
|
|
||||||
// var tsTurnSet = DateTime.Now - GotoTurnSetTime;
|
|
||||||
// if (tsTurnSet.TotalSeconds > 3)
|
|
||||||
// {
|
|
||||||
// PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
|
||||||
// {
|
|
||||||
// Bunki = arDev.Narumi.eBunki.Left,
|
|
||||||
// Direction = moveDir,
|
|
||||||
// PBSSensor = 1,
|
|
||||||
// Speed = arDev.Narumi.eMoveSpd.Low,
|
|
||||||
// });
|
|
||||||
// GotoTurnSetTime = DateTime.Now;
|
|
||||||
// PUB.log.Add("Turn Bunki Set");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// PUB.mapctl.Manager.agv.CurrentRFID.TurnOK = false;
|
|
||||||
// PUB.mapctl.Manager.agv.CurrentRFID.TurnStart = DateTime.Now;
|
|
||||||
// PUB.sm.UpdateRunStepSeq(); //셋팅이 맞으니 다음스텝으로 진행한다
|
|
||||||
// GotoTurnStep += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else PUB.sm.UpdateRunStepSeq();
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
//else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
//{
|
|
||||||
// //턴이완료되길 기다린다.
|
|
||||||
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
|
|
||||||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
|
|
||||||
// {
|
|
||||||
// //(최소5초는 기다리고 판단한다)
|
|
||||||
// if (stepTime.TotalSeconds < 5) return false;
|
|
||||||
|
|
||||||
// //최대30초는 기다려준다
|
|
||||||
// if (stepTime.TotalSeconds > 30)
|
|
||||||
// {
|
|
||||||
// var ermsg = "Turn Timeout(30sec)";
|
|
||||||
// PUB.log.AddE(ermsg);
|
|
||||||
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.TurnTimeout, ermsg);
|
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //모션이 멈추었다면 턴이완료된것이다.
|
|
||||||
// if (PUB.AGV.system1.agv_stop)
|
|
||||||
// {
|
|
||||||
// if (PUB.AGV.system1.Mark1_check == false && PUB.AGV.system1.Mark2_check == false)
|
|
||||||
// {
|
|
||||||
// PUB.log.AddE($"Turn 완료이나 Mark 센서가 확인되지 않았습니다");
|
|
||||||
// }
|
|
||||||
// GotoTurnStep += 1;
|
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// //아직 이동중이므로 대기한다
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else PUB.sm.UpdateRunStepSeq(); //기타사항은 다음으로 넘어간다
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
//else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
//{
|
|
||||||
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
|
|
||||||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
|
|
||||||
// {
|
|
||||||
// if (GotoTurnStep < 2)
|
|
||||||
// {
|
|
||||||
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.TurnError, "턴시퀀스 완료 실패");
|
|
||||||
// PUB.log.AddE($"턴완료시퀀스가 2가아닙니다. 대기 상태로 강제 전환합니다");
|
|
||||||
// PUB.sm.SetNewRunStep(ERunStep.READY);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// PUB.log.AddI("Turn Complete");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //방향전환용 턴이라면 이동기록을 추가해서 방향이 맞도록 처리해주자
|
|
||||||
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove)
|
|
||||||
// {
|
|
||||||
// var rfid = PUB.mapctl.Manager.agv.CurrentRFID;
|
|
||||||
// var lastHistory = PUB.mapctl.Manager.agv.MovementHistory.Last();
|
|
||||||
// //원래방향에서 반대로 처리한다
|
|
||||||
// var revDir = lastHistory.Direction == AGVControl.AgvDir.Backward ? AGVControl.AgvDir.Forward : AGVControl.AgvDir.Backward;
|
|
||||||
// PUB.mapctl.Manager.agv.AddToMovementHistory(rfid.Value, rfid.Location, revDir);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// //이동용 RFID에서 턴명령이 들어있는경우였다
|
|
||||||
// PUB.mapctl.Manager.agv.CurrentRFID.TurnEnd = DateTime.Now;
|
|
||||||
// PUB.mapctl.Manager.agv.CurrentRFID.TurnOK = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// PUB.sm.UpdateRunStepSeq();
|
|
||||||
// }
|
|
||||||
// else PUB.sm.UpdateRunStepSeq();
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//좌턴이동명령 전송
|
|
||||||
|
|
||||||
//마크스탑전송
|
|
||||||
|
|
||||||
//마크스탑이 확인되면 나머지는 경로예측에 맡긴다
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Project.StateMachine;
|
|
||||||
using COMM;
|
|
||||||
using AR;
|
|
||||||
|
|
||||||
namespace Project
|
|
||||||
{
|
|
||||||
public partial class fMain
|
|
||||||
{
|
|
||||||
|
|
||||||
public Boolean _SM_RUN_GOUP(bool isFirst, TimeSpan stepTime)
|
|
||||||
{
|
|
||||||
if (runStepisFirst)
|
|
||||||
{
|
|
||||||
// VAR.BOOL[eVarBool.FLAG_NEXTSTOP_MARK] = false;//);
|
|
||||||
VAR.BOOL[eVarBool.FLAG_NEXTSTOP_ALIGN] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//HW 연결오류
|
|
||||||
if (PUB.AGV.IsOpen == false)
|
|
||||||
{
|
|
||||||
PUB.Result.SetResultMessage(eResult.Hardware, eECode.AGVCONN, eNextStep.ERROR);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//충전 상태가 OFF되어야 동작하게한다
|
|
||||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//라이더멈춤이 설정되어있다면 음성으로 알려준다 200409
|
|
||||||
if (PUB.AGV.system1.stop_by_front_detect == true)
|
|
||||||
{
|
|
||||||
var tsSpeak = DateTime.Now - LastSpeakTime;
|
|
||||||
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.전방에물체가감지되었습니다);
|
|
||||||
LastSpeakTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//현재 위치가 결정되어있는지 체크한다
|
|
||||||
if (_SM_RUN_POSCHK(isFirst, stepTime) == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var idx = 1;
|
|
||||||
if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
//상차 가능 조건 확인
|
|
||||||
if (PUB.Result.TargetPos == ePosition.NONE)
|
|
||||||
{
|
|
||||||
PUB.Result.SetResultMessage(eResult.Hardware, eECode.NOTALLOWUP, eNextStep.ERROR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PUB.AddEEDB($"상차작업시작({PUB.Result.TargetPos})");
|
|
||||||
PUB.Speak(Lang.상차작업을시작합니다);
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
//모션 전후진 제어
|
|
||||||
if (UpdateMotionPositionForMark("GOUP"))
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
//장비멈춤확인
|
|
||||||
if (PUB.AGV.system1.agv_run == true)
|
|
||||||
{
|
|
||||||
var ts = DateTime.Now - LastCommandTime;
|
|
||||||
if (ts.TotalMilliseconds > 2000)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOUP");// PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GOUP:위치확정");
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//움직이지 않고 있다면 다시 이동을 시켜준다.
|
|
||||||
PUB.log.Add("이동 정지 확인");
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.system1.agv_run == true)
|
|
||||||
{
|
|
||||||
var ts = DateTime.Now - LastCommandTime;
|
|
||||||
if (ts.TotalMilliseconds > 2000)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOUP");// PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GOUP:위치확정");
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//커버를 자동으로 내려준다
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
UpdateProgressStatus(stepTime.TotalSeconds, 5, Lang.안전커버를내립니다);
|
|
||||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_DOWN] = true;
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.system1.agv_run == true)
|
|
||||||
{
|
|
||||||
var ts = DateTime.Now - LastCommandTime;
|
|
||||||
if (ts.TotalMilliseconds > 2000)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOUP");// PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GOUP:위치확정");
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//커버 내림이 완료될때까지 기다린다
|
|
||||||
if (true)
|
|
||||||
{
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_DOWN] = false;
|
|
||||||
PUB.Result.NextPos = ePosition.NONE;
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
//경과시간이 10초가 지나면 5초마다 음성을 출력한다
|
|
||||||
var tsCover = DateTime.Now - CoverControlTime;
|
|
||||||
if (tsCover.TotalSeconds >= 7)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.안전커버를내려주세요);
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.system1.agv_run == true)
|
|
||||||
{
|
|
||||||
var ts = DateTime.Now - LastCommandTime;
|
|
||||||
if (ts.TotalMilliseconds > 2000)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOUP");// PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GOUP:위치확정");
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//상차는 여기서 수량확인한다
|
|
||||||
if (PUB.Result.TargetPos == ePosition.F1)
|
|
||||||
PUB.counter.CountUp1 += 1;
|
|
||||||
else if (PUB.Result.TargetPos == ePosition.F2)
|
|
||||||
PUB.counter.CountUp2 += 1;
|
|
||||||
else if (PUB.Result.TargetPos == ePosition.F3)
|
|
||||||
PUB.counter.CountUp3 += 1;
|
|
||||||
else if (PUB.Result.TargetPos == ePosition.F4)
|
|
||||||
PUB.counter.CountUp4 += 1;
|
|
||||||
//else if (PUB.Result.TargetPos == ePosition.QA)
|
|
||||||
// PUB.counter.CountQA += 1;
|
|
||||||
PUB.counter.Save();
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
|
|
||||||
//IO업데이트 간격 전송
|
|
||||||
UpdateProgressStatus(stepTime.TotalSeconds, 5, Lang.안전커버를올려주세요);
|
|
||||||
PUB.Speak(Lang.안전커버를올리면상차가완료됩니다);
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_UP] = true;
|
|
||||||
PUB.Result.NextPos = ePosition.NONE;
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (PUB.sm.RunStepSeq == idx++)
|
|
||||||
{
|
|
||||||
if (PUB.AGV.system1.agv_run == true)
|
|
||||||
{
|
|
||||||
var ts = DateTime.Now - LastCommandTime;
|
|
||||||
if (ts.TotalMilliseconds > 2000)
|
|
||||||
{
|
|
||||||
PUB.AGV.AGVMoveStop("SM_RUN_GOUP");// PUB.PLC.Move(Device.PLC.Rundirection.Stop, "GOUP:위치확정");
|
|
||||||
LastCommandTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//커버 올림이 완료될때까지 기다린다
|
|
||||||
if (VAR.BOOL[eVarBool.FLAG_LIMITHIGH] == true)
|
|
||||||
{
|
|
||||||
VAR.BOOL[eVarBool.WAIT_COVER_UP] = false;
|
|
||||||
VAR.BOOL[eVarBool.ITEMON] = true;
|
|
||||||
PUB.sm.UpdateRunStepSeq();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//경과시간이 10초가 지나면 5초마다 음성을 출력한다
|
|
||||||
var tsCover = DateTime.Now - CoverControlTime;
|
|
||||||
if (tsCover.TotalSeconds >= PUB.setting.doorSoundTerm)
|
|
||||||
{
|
|
||||||
PUB.Speak(Lang.안전커버를올려주세요);
|
|
||||||
CoverControlTime = DateTime.Now;
|
|
||||||
|
|
||||||
////한쪽이 올라가 있는 상태에..
|
|
||||||
//if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU) == true)
|
|
||||||
//{
|
|
||||||
// //모터는 올리는 방향일때에...
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_LDIR) == true)
|
|
||||||
// {
|
|
||||||
// //모터가 멈춰있을때에..
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_LRUN) == false)
|
|
||||||
// {
|
|
||||||
// //자동으로 올려준다 (센서가 간혹 인식이 안되어서 .대기하는 경우가 잇음)
|
|
||||||
|
|
||||||
// //왼쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU) == false)
|
|
||||||
// PUB.PLC.ZMot_Left(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
|
|
||||||
// //오른쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU) == false)
|
|
||||||
// PUB.PLC.ZMot_Right(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU) == true)
|
|
||||||
//{
|
|
||||||
// //모터는 올리는 방향일때에...
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_RDIR) == true)
|
|
||||||
// {
|
|
||||||
// //모터가 멈춰있을때에..
|
|
||||||
// if (PUB.PLC.GetValueO(arDev.FakePLC.DOName.PINO_GUIDEMOTOR_RRUN) == false)
|
|
||||||
// {
|
|
||||||
// //자동으로 올려준다 (센서가 간혹 인식이 안되어서 .대기하는 경우가 잇음)
|
|
||||||
|
|
||||||
// //왼쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU) == false)
|
|
||||||
// PUB.PLC.ZMot_Left(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
|
|
||||||
// //오른쪽이 올라가 있지 않은 경우
|
|
||||||
// if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU) == false)
|
|
||||||
// PUB.PLC.ZMot_Right(arDev.FakePLC.ZMotDirection.Up);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PUB.AddEEDB($"상차작업완료({PUB.Result.TargetPos})");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user