Compare commits

...

10 Commits

Author SHA1 Message Date
backuppc
4e9d29d22f ing... 2025-12-12 17:27:50 +09:00
backuppc
07ddc0425f 에뮬레이터 개발 전. 2025-12-12 14:29:06 +09:00
backuppc
ee2a6f04bb add ignore ( .vscode) 2025-12-11 08:24:08 +09:00
backuppc
6024f372d3 buffer in/out 시퀀스 작성중 2025-12-11 08:22:52 +09:00
backuppc
9a0a389e07 buffer in/out 시퀀스 작성 중 2025-12-11 08:22:24 +09:00
ChiKyun Kim
af280d7b27 오디오재생함수추가 2025-12-10 13:36:35 +09:00
backuppc
868fa2deec add supertonic 2025-12-10 11:39:15 +09:00
backuppc
3695ab0044 sync 전용 화면 만들기 전 백업. 2025-12-10 10:35:19 +09:00
backuppc
2236e3b1ba add GDS set button 2025-12-09 13:33:42 +09:00
backuppc
9db031c305 agv 노드 정보 정리 세분화 2025-12-09 13:18:22 +09:00
112 changed files with 64083 additions and 3537 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
obj
bin
.vscode
*.user
*.v12
*.suo

View File

@@ -0,0 +1,139 @@
<?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>..\arControl.Net4.dll</HintPath>
</Reference>
<Reference Include="arFrameControl">
<HintPath>..\arFrameControl.dll</HintPath>
</Reference>
<Reference Include="ArLog.Net4">
<HintPath>..\ArLog.Net4.dll</HintPath>
</Reference>
<Reference Include="ArSetting.Net4">
<HintPath>..\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\EnigProtocol\enigprotocol\ENIGProtocol.csproj">
<Project>{9365803b-933d-4237-93c7-b502c855a71c}</Project>
<Name>ENIGProtocol</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

587
AGVEmulator/DevAGV.cs Normal file
View File

@@ -0,0 +1,587 @@
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 eCommand : byte
{
LOAD = 0, //EEPROM 불러오기
SAVE, //EEPROM 저장
RESET, //초기화
PINGCHK,
SET_PINMODE, //PINMODE 설정
SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0])
SET_AOUTPUT, //아날로그출력설정(포트GET_SETTING = 50, //셋팅값 요청
SET_FLAG,
SET_EEPROM,
SET_MANUALSPEED,
GET_SETTING = 50,
GUIDE_MOT = 90, //가이드커버(양쪽) 0=멈춤,1=UP,2=DN 아스키코드표 90=Z
SET_EEP_DIREV,
};
public enum DOName
{
PINO_GUIDEMOTOR_LDIR,
PINO_GUIDEMOTOR_LRUN,
PINO_GUIDEMOTOR_RDIR,
PINO_GUIDEMOTOR_RRUN,
PINO_EMPTY_26,
PINO_EMPTY_27,
PINO_EMPTY_28,
PINO_EMPTY_29,
}
public enum DIName
{
PINI_EMG,
PINI_BTN_1,
PINI_BTN_2,
PINI_BTN_3,
PINI_BTN_4,
PINI_OVERLOADL,
PINI_OVERLOADR,
PINI_EMPTY_36,
PINI_EMPTY_37,
PINI_EMPTY_38,
PINI_BTN_ZUP,
PINI_BTN_ZDN,
PINI_LIMIT_LU,
PINI_LIMIT_LD,
PINI_LIMIT_RU,
PINI_LIMIT_RD,
PINI_STOP,
}
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
View 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
View 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();
}
}
}

View 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")]

View 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;
}
}
}
}

View 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>

View 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;
}
}
}
}

View 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
View 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
View 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);
}
}
}
}

View 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;
}
}
}

View 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
}
}
}
}
}

Binary file not shown.

Binary file not shown.

37
AGVEmulator/UC/AgvViewer.Designer.cs generated Normal file
View 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
View 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
View 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;
}
}

View 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();
}
}
}

View File

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

3
AGVEmulator/app.config Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

943
AGVEmulator/fMain.cs Normal file
View 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 List<MapNode> _mapNodes;
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);
_mapNodes = mapresult.Nodes;
_agvCanvas.Nodes = _mapNodes;
_agvCanvas.FitToNodes();
// Initialize Visual AGV
if (_mapNodes.Count > 0)
{
_visualAgv = new VirtualAGV("AGV01", _mapNodes[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 || _mapNodes == null) return;
// Find node by tag
// Assuming NodeId might be the tag or contain it
var node = _mapNodes.FirstOrDefault(n => n.NodeId == tag || n.NodeId.EndsWith(tag));
// If not found, try to parse tag as int and match
if (node == null && int.TryParse(tag, out int tagNum))
{
node = _mapNodes.FirstOrDefault(n => n.NodeId == 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 || _mapNodes == 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 _mapNodes)
{
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.NodeId != numericUpDown1.Text)
{
if (int.TryParse(node.NodeId, out int tag))
{
AGV.SendTag(node.NodeId);
numericUpDown1.Text = node.NodeId;
// 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, MapNode e)
{
if (e != null && _visualAgv != null)
{
_visualAgv.CurrentPosition = e.Position;
_currentPosF = e.Position;
if (int.TryParse(e.NodeId, 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";
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}");
_mapNodes = result.Nodes;
_currentMapFilePath = filePath;
// RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용
// 시뮬레이터 캔버스에 맵 설정
this._agvCanvas.Nodes = _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) && _mapNodes != null && _visualAgv != null)
{
var node = _mapNodes.FirstOrDefault(n => n.NodeId == input);
if (node != null)
{
_visualAgv.CurrentPosition = node.Position;
_currentPosF = node.Position;
numericUpDown1.Text = node.NodeId;
// Auto-orient: Prefer Right (0) or Up (270)
var neighbors = _mapNodes.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
View 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>

View File

@@ -36,6 +36,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVLogic\AG
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVLogic\AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "SubProject\commutil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Supertonic.Netfx48", "SubProject\SuperTonic\Supertonic.Netfx48.csproj", "{19675E19-EB91-493E-88C3-32B3C094B749}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -166,6 +170,30 @@ Global
{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.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
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.ActiveCfg = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.Build.0 = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.ActiveCfg = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.Build.0 = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.Build.0 = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.ActiveCfg = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.Build.0 = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.ActiveCfg = Release|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -178,6 +206,8 @@ Global
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C} = {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}
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{19675E19-EB91-493E-88C3-32B3C094B749} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}

View File

@@ -62,7 +62,6 @@
<Compile Include="Models\ImagePathEditor.cs" />
<Compile Include="Models\MapImage.cs" />
<Compile Include="Models\MapLabel.cs" />
<Compile Include="Models\NodePropertyWrapper.cs" />
<Compile Include="Forms\MainForm.cs">
<SubType>Form</SubType>
</Compile>

View File

@@ -64,6 +64,9 @@ namespace AGVMapEditor.Forms
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.btnSave = 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();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
@@ -351,7 +354,9 @@ namespace AGVMapEditor.Forms
this.btnClose,
this.toolStripSeparator3,
this.btnSave,
this.btnSaveAs});
this.btnSaveAs,
this.toolStripSeparator2,
this.toolStripButton1});
this.toolStrip2.Location = new System.Drawing.Point(0, 0);
this.toolStrip2.Name = "toolStrip2";
this.toolStrip2.Size = new System.Drawing.Size(1200, 25);
@@ -417,6 +422,28 @@ namespace AGVMapEditor.Forms
this.btnSaveAs.ToolTipText = "다른 이름으로 저장";
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
//
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.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripDropDownButton toolStripButton1;
private System.Windows.Forms.ToolStripMenuItem allTurnLeftRightCrossOnToolStripMenuItem;
}
}

View File

@@ -221,8 +221,8 @@ namespace AGVMapEditor.Forms
toolStripStatusLabel1.Text = $"{nodes.Count}개 노드 선택됨 - PropertyGrid에서 공통 속성 일괄 변경 가능";
// 다중 선택 PropertyWrapper 표시
var multiWrapper = new MultiNodePropertyWrapper(nodes);
_propertyGrid.SelectedObject = multiWrapper;
//var multiWrapper = new MultiNodePropertyWrapper(nodes);
_propertyGrid.SelectedObjects = nodes.ToArray();// multiWrapper;
_propertyGrid.Focus();
}
}
@@ -833,11 +833,10 @@ namespace AGVMapEditor.Forms
foreColor = Color.Black;
backColor = Color.White;
break;
case NodeType.Rotation:
foreColor = Color.DarkOrange;
backColor = Color.LightYellow;
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
foreColor = Color.DarkGreen;
backColor = Color.LightGreen;
break;
@@ -975,9 +974,7 @@ namespace AGVMapEditor.Forms
return;
}
// 노드 래퍼 객체 생성 (타입에 따라 다른 래퍼 사용)
var nodeWrapper = NodePropertyWrapperFactory.CreateWrapper(_selectedNode, _mapNodes);
_propertyGrid.SelectedObject = nodeWrapper;
_propertyGrid.SelectedObject = _selectedNode;
_propertyGrid.Focus();
// 이미지 노드인 경우 편집 버튼 활성화
@@ -989,8 +986,7 @@ namespace AGVMapEditor.Forms
/// </summary>
private void ShowCanvasProperties()
{
var canvasWrapper = new CanvasPropertyWrapper(_mapCanvas);
_propertyGrid.SelectedObject = canvasWrapper;
_propertyGrid.SelectedObject = _mapCanvas;
DisableImageEditButton();
}
@@ -1124,7 +1120,9 @@ namespace AGVMapEditor.Forms
UpdateTitle();
// 🔥 다중 선택 여부 확인 및 선택된 노드들 저장
bool isMultiSelect = _propertyGrid.SelectedObject is MultiNodePropertyWrapper;
bool isMultiSelect = _propertyGrid.SelectedObjects is MapNode[]; // _propertyGrid.SelectedObject is MapNode[];
//var a = _propertyGrid.SelectedObject;
List<MapNode> selectedNodes = null;
if (isMultiSelect)
{
@@ -1146,9 +1144,9 @@ namespace AGVMapEditor.Forms
// 🔥 다중 선택인 경우 MultiNodePropertyWrapper를 다시 생성하여 바인딩
if (isMultiSelect && selectedNodes != null && selectedNodes.Count > 0)
{
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
_propertyGrid.SelectedObject = multiWrapper;
// System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
//var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
_propertyGrid.SelectedObjects = selectedNodes.ToArray();// multiWrapper;
}
// PropertyGrid 새로고침
@@ -1180,18 +1178,18 @@ namespace AGVMapEditor.Forms
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;
}
//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;
foreach (var node in _mapNodes)
@@ -1370,5 +1368,46 @@ namespace AGVMapEditor.Forms
#endregion
private void allTurnLeftRightCrossOnToolStripMenuItem_Click(object sender, EventArgs e)
{
//모든노드의 trun left/right/ cross 속성을 true로 변경합니다
if (_mapNodes == null || _mapNodes.Count == 0)
{
MessageBox.Show("맵에 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var result = MessageBox.Show(
$"모든 노드({_mapNodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" +
"• CanTurnLeft = true\n" +
"• CanTurnRight = true\n" +
"• DisableCross = false",
"일괄 속성 변경",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
foreach (var node in _mapNodes)
{
node.CanTurnLeft = true;
node.CanTurnRight = true;
node.DisableCross =false;
node.ModifiedDate = DateTime.Now;
}
_hasChanges = true;
UpdateTitle();
RefreshMapCanvas();
MessageBox.Show(
$"{_mapNodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.",
"완료",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
UpdateStatusBar($"모든 노드의 회전/교차 속성 활성화 완료");
}
}
}
}

View File

@@ -120,22 +120,40 @@
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>132, 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>
<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">
<value>462, 17</value>
</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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBNiBJhHMfx/ykKiohQ18tEhygyWGQRJwh1
fcHAt7qJ3TOj6FJ0akP06G2RPQd1MNhKcxvCxDc2UHbXSCoWNu8dWp3ReTks/moGVnafjW33c/rPM8/3
4eEhOoLJZ/6UvDp7l10/lNKa4+TW3AN9Hq9eC8kNW1ef1QZ3UapzRztMbs1+lVs2Qa5fLqsbtzBucE9G
NetwVD2fYPf+07h5ZUH9koD24yG0rWdQ1kMQK2eVXzXTaXbv1Lh9dUZu2apy4xLUjSi0nxk0X0bRLd7B
Vu0elM48xE8nIX6kJbbdZ1znFpTubTRfxaBpGiaTCdbfJaB+v4+BQN+2y3SBbaak5oxJqlqEcDiMTqcD
VVXR6/VQexGCtvkYYplG4gfi2c6gVKzcqHKuGIvFkEwmkcvlUCgUUF6ah7b5FIMV2hFXaG1YoudsOxWN
Ro2Y53l4vV5EIhHIayFIVSsk4Qy2S8SxzdRu7HQ64fF4YDab9SsvDt7Tjlii62KRusM3lGI7QzweRzab
NWK32w2TyQR9XSrRDfEtCfqM13Ti9zLdZFsKBoPIZDIH4l3DZQrs/d7H5/MhnU7D4XDA5XIdiA+VSqXQ
7/dht9sRCASOF+v0A9rtNvL5vPFg7P//8vv9j/RrWyyW48d//QFpDAsmbMS+zgAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBNiBJhHMfx/ykKKohQ18tEhygyWGQRJwh1
fcHAt7qJ3TOj6FJ0akP06C1kz0EdDLbS3IbaxJdhA2V3jaRiYfPeIXPGeTss/mqElfbZ2HY/p/8883wf
Hh6iAxh/5I+pq7M32fV9aeIcp4pzd8xZWb0UUZuOrjnrTe6s3OAOdpgqzn5WRYegNs5X9Y1rUJrcg1Hd
PhzVTqfYvf+ktC4s6J9SML7dhbH1CNp6BMP3J7Ufdctxdu+U0r44o4qOmto8B30jDuN7Dq1ncXTLN7BV
vwWtMw/pw1FI72iRbXdRGtyC1r2O1vMEDMPAeDzG+usU9K+38UugL4MqnWGbKbk1Y5FrNiEajaLT6UDX
dfR6PdSfRmBs3odUpZH0lni2m9BW7Nxo5VQ5kUggnU6jUCigVCqhujgPY/MhBsu0LS3T2rBCj9l2Kh6P
T2Ke5+H3+xGLxaCuRSDX7JCFExhUiGObqZ3Y7XbD5/PBarWaV34yeEPbUoUuS2XqDl9Shu0mkskk8vn8
JPZ6vbBYLDDX5QpdkV6RYM54QUd+LtFVtqVwOIxcLrcn3jFcotDf37sEAgFks1m4XC54PJ498b4ymQz6
/T6cTidCodDhYpN5QLvdRrFYnDwY+/+/gsHgPfPaNpvt8PEfvwFcBQshDC9YfwAAAABJRU5ErkJggg==
</value>
</data>
<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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHMSURBVDhPnZJfa9pQGMb7JXa7sW9RCP1ku1lXRi+3luJa
dteOjYi5CJhoBIPGLYkExb8zic5molPnoAO3k80ozzhHTopzq2wPhHDe8z6/5yV599rtNlqtFhqNBur1
Omq1GqrVKiqVCn3f29ulZrOJ1Wq19QyHQwYxDONuCE2mhsFgwJKDIIghtOY4zt0QOjJtpmn0XC6XK/P5
HFEUsToFWpb1dwhN5Y22bf80TXMQhiE+f/qOd9qU3fX7fZRKpT9DaDIfeblcgpq/TEO8OPJx8sjD2+wa
0uv1UCgUoKrqJsRxHDKbzVjTYrFgyYknPi6f9fH6LMD5UTeG+L6PfD6/CbFt+8CyLDKdrpvc+g1Lvjq5
hnj+cQvieR40TduEmKYpFItFMplMWFO39RWJQw+vTm8hiUMfhrq+73Q6UBQFoijeQgzDEHRdJ+PxeAvy
JhHg5XEPF8fvEUVLNkUmk0EqlXoQA6h0XRdyuRwZjUYx5Oyxi4unXVyddvFtHsZmURQfbpi5NE0Tstks
oZvIPlzzBpfPP+BHGO02cymKIqTTaUI3kf9ialZVdbeZS5blfVmWCV0w13X/zcwlSdK+JEnkv8xcFJJM
Ju//Xuf6Be3RFseUK14pAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfb9JgFMb3JbzV+C2WNPtk3jgXs0vdsuAW
7zajKaEXTWjhvShpilBIA+GvtAWxIyAgJjNBW6WQx5yXtBPREX2SpnnPe57fc9KevXa7jVarhUajgXq9
jlqthmq1ikqlQu97e7vUbDaxWq22nuFwyCGGYdwNoWQyDAYDnux5XgyhmmVZd0NoZGqmNDqXy+XKfD5H
GIa8TkDTNP8OodRfGn8UCoVBEAT49PEbCmzK7/r9PvL5/J8hlByNvFwuQebP0wAvjlycPHLwNruG9Ho9
6LoOVVU3IZZl+bPZjDctFguenHji4vJZH6/PPJwfdWOI67rQNG0TUiqVDkzT9KfTdZNdv+HJVycfIJ5f
b0EcxwFjbBNSLBYFXdf9yWTCm7qtL0gcOnh1egtJHLow1PV9p9OBoigQRfEWYhiGoGmaPx6PtyBvEh5e
HvdwcfwOYbjkU2QyGaRSqQcxgJTL5QTGmD8ajWLI2WMbF0+7uDrt4us8iM2iKD7cMEdijAnZbNanTeQf
rnmDy+fv8T0Id5sjKYoipNNpnzYx+sVkVlV1tzmSLMv7siz7tGC2bf+bOZIkSfuSJPn/ZY5EkGQyef/3
eqSfzGgWuCCdbTAAAAAASUVORK5CYII=
</value>
</data>
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG8SURBVDhPnZLditpQFIXnJXrb0rcYCH2wXhSZwly2U0rp
O0TMRUp+BTMaIYkERY3BH0TEalW8KEV6gp4cVjlnOBms7Ui7IITsvde3NmRfpWmKwWCAfr+PXq+HbreL
TqeDdrvN38+uLilJEjDGzp7lcikgvu8/DeHJ3LBYLETyfD4vILwWx/HTEL4yH+Zp/LvVarX3+z0opaLO
gWEY/h3CU+VgFEWHIAgWWZaBfv8GmjqiN5vN0Gw2/wzhyXLlPM/BzezHFrl9g/zLa9DUFr3pdIp6vQ7L
sk4hcRyT3W4nho7Ho0jOrRKY9w6s8RHMvikgk8kEtVrtFBJF0aswDMl2u32AfO2KZHZ/B9b8fAYZj8dw
XfcUEgSB0mg0yGazeYCsElCzBHb/4RFilUATQ/SHwyFM04Sqqo8Q3/cVz/PIer0+h/ifwNxb/DRvwXIq
trBtG5VK5UUB4PI8T6lWq2S1WhWQo/EGzHmLrHaHA9kXZlVVX56YpVzXVRzHIfwSBWTZB/Xegx2yy2Yp
0zQVwzAIv0T5i7nZsqzLZild1691XSf8wEaj0b+ZpTRNu9Y0jfyXWYpDyuXy89/rUr8A4v0dufOO1X4A
AAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYCH2wXhSZwly2U0rp
O0TMRUr+zkUyaUSjBEWNwR9ExGpVvCiDzAl6cljlnCGZWtuRdkEI2Xuvb23IvkiSBP1+H71eD91uF51O
B+12G61WS7yfXZxTHMfgnJ88i8VCQoIgeBoikoVhPp/L5NlsVkBELYqipyFiZTEs0sR3s9ls7XY7MMZk
XQDDMPw7RKT+Mriv1WrzNE3BfnwHSxzZm06nqFarf4aI5HzlLMsgzPxug8y+QvblNVhiy95kMoHv+7As
6xgSRRHdbrdy6HA4yOTMKoF778C/fgS3rwrIeDyG67rHkEaj8SoMQ7rZbB4g3zoymd/egFc/n0BGoxEI
IceQer2u+L5P1+v1A2QZg5kl8NsPjxCrBBYbsj8YDGCaJlRVfYQEQaC4rktXq9UpJPgETq5xb16DZ0xu
Yds2KpXKiwIg5HmeQgihy+WygByMN+DOW6TuDfZ0V5hVVX15ZM5FCFEcx6HiEiVk0QPz3oPv0/PmXKZp
KoZhUHGJ+S8WZsuyzptz6bp+qes6FQc2HA7/zZxL07RLTdPof5lzCUi5XH7+ez3XT8GUHaqSv5fjAAAA
AElFTkSuQmCC
</value>
</data>
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHeSURBVDhPnZLda9NgFMb3T3ir+F8Mwv6e3cgcorfToYgK
Or/unF94ldJsZm3SJq4xTSFJDSlt09oPSilpa1sKilbfsn7wyPtCInG6og+EkHPO83sO5KxVq1VUKhWU
y2WUSiUUi0UUCgW4rkvf59ZWyfM8LJfLU0+v12MQwzDOhtBkavB9nyV3Op0QQmuO45wNoSvTYZpGv/P5
vDuZTDCfz1mdAi3L+juEpgaDtm2fmKbpT6dTjL50kfUOWa/dbiOXy/0ZQpODlReLBah5/PUTHia2cfdg
E7p3wHqtVgu6rkOW5SjEcRwyHo/Z0Gw2w/BzF3uJy3j2bgcvtZt4lNgOIc1mE5lMJgqxbXvDsiwyGo3Y
0Ef/A+4dbuL58Q280e/glXYrAmk0GlAUJQoxTZPLZrNkOByyoXq3gAfiFl4c74aQvaMtZEox1q/VapAk
CTzP/4IYhsFpmkYGg8EpyOv3t/FUvoonR9cwX8zYFqlUCvF4/EIIoNI0jVNVlfT7/RBy/+0lPE5ewb66
i+8/voVmnucvRsyBFEXh0uk0oZfI1vVd7KvXMT0hq82BJEnikskkoZcY/GJqlmV5tTmQKIrroigSemD1
ev3fzIEEQVgXBIH8lzkQhcRisfO/1wP9BF1iD3Q2XAPzAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Mgn/PbmQO0Vt1KKKC
zo/dbX7hVUqzmbVJ+84lxpQ2rSGlbVr7QSklbW1LQdHqW9YPHnlfSDROV/SBEHLOeX7PgZyVSqWCcrmM
UqmEYrGIQqGAfD4Px3HY+8zKMrmui8ViceLpdrscYprm6RCWzAye5/HkdrsdQFjNtu3TIWxlNszS2Hcu
l3PG4zFmsxmvM6BlWX+HsNRfBo/T6bQ3mUww/NzBO3ef91qtFlKp1J8hLNlfeT6fg5lHXz7iYWwDd/fW
YLh7vNdsNmEYBlRVDUNs26aj0YgPTadTDD51sBW7hJ031/Bcv4lHsY0A0mg0oGlaGJLNZi9YlkWHwyEf
+uC9x739NTw9uoFXxh280G+FIPV6HYSQMCSTyQiGYdDBYMCHap08HsjreHa0GUC2DtahFSO8X61WoSgK
RFH8CTFNU9A0jfb7/ROQl29vY1u9gicHVzGbT/kWiUQC0Wj0XABg0nVdIITQXq8XQO6/vojH8cvYPdzE
t+9fA7MoiudDZl+EECGZTFJ2iXxdz8Hu4XVMjulysy9FUYR4PE7ZJfq/mJlVVV1u9iXL8qosy5QdWK1W
+zezL0mSViVJov9l9sUgkUjk7O91Xz8AO/kPZbC5CrgAAAAASUVORK5CYII=
</value>
</data>
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGpSURBVDhP7Y87TyJRGIZR9AdYiYWFMdb+AYvNopXRWJho
oY1aWGyzW8DuFmplobGxMxkPrVrY4f0SHJhhmBmGAYMxiIjcxkGh8ZZoeM05UWPG6w/wTZ7m5Hve8302
23dYPB6PkxASJITgiyQ5jut+LiCExFVVRDabYORylCNGPp9kFArHMIwUDOMEsZhKSzIvC8xUKg5B8MPn
8zF4noff70cgEIAgCAgGg5AkCaqqshKO424sBQeQJJGJVikUCkFRFCbregSmmXldkE4fQlFkiKLIRCrJ
ssykcDiMSCQCXdexvx9DsZh7XXB6moCmqUyiv1FJ0zQmRqNRKHNj2BtswZazFrv9jVj83Xv3JGNnZxPn
53lcXFAKKJUMlMtnj5hILE4j6mrDjXcGlfgarhb+QP7ViiX3AFjBZ3h7HLj2zgCzXYC7DphsQnHqB1Z6
6vF8xkfZ+FlTqajLeJnyuAP03Tr7Ztbb7ZnL+WFg3IFblw0llw3pUTvWOuxZ6+yb4fsaJqSh5ru8uwnm
v1okR6qw3Vlzv9pe/d86+274vsa/6057iq5NN3qSHwBTm6pq5+CDSAAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y+/TxphGMdR4A9wKh0cNHHuP9ChKXQxsWEw
oYMOjR06dGg6QNuhdHLQsLgZ8GVFBzcqRGtDD+447o7jwGAMIkV+ncevpa0mGr7N+0YNOX+0f0C/yWd5
83y+7/NYLP/DEg6HnYSQNCEE/0g5FAo9vy4ghBQVRUC9XmI0GpRDRrNZZrRaR9D1CnT9BwoFhZbUhguM
SqUInk8ikUgwOI5DMplEKpUCz/NIp9MQRRGKorCSYDB4airYhygKTDRLmUwGsiwzWdNyMIzazYJq9QCy
LEEQBCZSSZIkJmWzWeRyOWiahr29Atrtxs2C4+MSVFVhEv2NSqqqMjGfz0Ne/YTv81PYcdrx7cU4Im9n
z69k7O5uo9NpotultNDr6ej3Ty4xUFpfRt77GKfRAAbFGH5F3kF68wgbvjmwgr8RdTvwOxoAVmYA3xiw
OIH20hN8cT/A9Rn3Jf7UNhgomxhO3+8AfTfP3pq4y1r7ubYA+B0481rQ81pQfW1F7Jm1bp69NZzn4Wfx
5eR50zcB44Md5Vcj+Dptu9hyjX40z94ZzjP+Pu60VujadKMr+Q87TKpZvTdvaAAAAABJRU5ErkJggg==
</value>
</data>
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLfT9NXGMdx/oPdL17twmQxi97sajfTzV+b
2Q91CxuxZGt0mWMxy3TFMhRx+hUqBVoI0FoqvxxrVSZFwHWtWuy0gy8UykAdiggMEGq/7bftkTB8L222
TojbJ3muznNeJznPk2V39ZfZ2+RZs1NWjC3PqeaVVemQFetFuTzrn9ja5bm5R4+eqvE48YT431pQBXOR
GDXOvkgGSL0cU+NUd0xS1hGk6PIPNPSZcQx+x7lgMVU+A5/XW/ja1oPONsJ8TGByDigZoLxFVlJ6zeVJ
pK5uGnrNtA4dxNr7GTW3tNQG9mNwH2d/XQt6+yhzynOAWEJQ1zVFUWcT3weLqQ3spfKXPRh8HyFdz8bo
+4pc02kKG+8wqyQxOftXAkpCYO2e5qirgbNyIRV+DdK1Dznm3cmRn3ciefLQVJRgb23idks2v186QdC2
hWD1Ok0aiMQFtit/ILk6MftOUe7XUuTdhf6ndznqyebbH/OpbjzEjFeHMtQB6jSRofP4S7fNpoHHqsDu
nsHcOUi+w47UfoBSz14k9ycUOL/AWJ/H/HARi5PtzFwzkAxd5M8HNxi07RNZqVmHY4IGz2waMbmCfFOs
4aTxTSTDRs6YthEeLmQ5eYPkvTzCv37JaJOWoCVnIWB+b20aWIgK9I530LXu4OC5t7AUvErIvpvh5s08
Dh1mWdwkMZaLeJhDbETPaN2OpwPlmzekP7GsuU8JRwW+sauZ8ofa6Ll0gJle3d+XNYiJj4kOF3Cv7m3O
Nl5QM1MwpoHkCsB7tYSRK4dYTvoQ41qePNyDEjrM3dqtTE2MU+l4ZoxVTnkiHIkSVQXR+BMGvE381qln
sKuCqcAHJMZzCAfzGTvzPgvT95lPr7L87ypb2+RSy/n+ntRypGRHyRsQe8Bty268+hfx127Hc/L15fpG
l5o6r77Qr1jb5IoMsDrHtS8vLd6sYjFQSeDERlo/XaN269atX933n9n12gtCyl275D61fcldtul6R/4r
L63ueTZ/AZ9Hi93BxLexAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL5SURBVDhPfdLfT5tVHMdx/gPvjVcmGpPF6I033pip000X
f+A0uGUlk0zjxCzGzbIiDpnbM6iUQiGUFqhd2YbtBo4yYMN2W4FtFR54oMimMhkDpA10fdqn7RlB9jZt
tA4y/STfq/M9r5Oc7zfP4R2tdnTI4TqPrJraHlKu9WV2y6q9Q67J+yfNXSORcCRyX0smSabE/9ayJojE
EjS6h2M5IPNyQkvS0D1HdbdC+fnvcY5YcI9/wymlgvqAkY9bbXzeMoC+ZYqlhKDWM6bmgJo2Wc3ojefn
kHr7cA5baJ84gH34IxqvF2EN7sPYf4R9TScxOG4QUR8CJFKCpt55yntcnFYqsAb3Unt1N8bA+0hXCjAF
PqOw7lvKTvxCWE1T6xldD6gpgb1vgcNeJ9/JZZiHdEiX3+Nrfz5f/ZiP5CtGZ67E0e7i5skCfjt3FKXl
FZSGTbosEEsKWi78geTtwRI4Ts1QEeX+dzBcfJPDvgK+/KGEhhMHWfTrUSe6QVsgNnGGwaqt4SxwVxM4
+hex9IxT4nYgde2nyrcXqX8PpZ5PMLUWszRZzspcF4uXjaRDHfx5exCl+UORl5l1NCFw+sJZpM6r8EWF
jmOml5GMm2mu20p0soy19CDpW8VEf/qUG64iFNvO5aDlrSezwHJcYHC/gb59OwdOvYat9DlCjh1Mtm3h
bugQa+IaqelCxJ1dJKYMTFm33x+r2fJs9hOrXSNqNC4ITF/K1VCok4Fz+1kc1v99WYeY3Ul8spRbTa/j
cJ7VclMwZYH0OsB/qZKpCwdZSwcQM0Xcu7MbNXSIX62vMj87g9n9wBjrPfJsNBYnrgniyXuM+V383GNg
vNfMfPBdUjO7iColTDe/zfLC7yxlV3nk31W2d8pVtjOjA5nlyMjuypcgcZubth34DY8yZN2G79gLa61O
r5Y5rz87qto7ZXMO2JgjHzy1unKtnpVgLcGjmzm95zGtT7/pmY19/5n85x8RUuETqxePb1vtr37xSnfJ
049v7HkwfwEdnYukisiEOQAAAABJRU5ErkJggg==
</value>
</data>
<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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHwSURBVDhP7Y7Ni1JhGMVvXadgWrWzRcTQKgXJVbQQx0/w
K7iL1FVBKdYrLW4fM9pdGCPMQpnAC+YqoY240VaTWhnouInIVubqziBFSJEGzaRN5olnIDFx/oMOvPBw
zu8cXo77r4lsNtsJxti9eDzey2azkCSpwxi7wBhTS5KkkEcZMcT+U9ZoNMdcLlcilUqh2+1CURTUajXE
YrFtSZLq7XZ7SF6j0QAxxFJnMqDX6y+Jovit1Wr1/H7/M4fDMQoGg5BlGdFoFHSTR1kul+sQS53pgcTa
2tpAluWQ2+1eNBgM36vVKprNJur1OgqFAsijjBhiqTMZ0Ol0N0OhUD+dTt8VBKFiMpn27XY7BEE4eHST
RxkxxFJnMqDVas95PJ52Mpns5/P5r8ViEZlMBqIoIhKJHNzldYZXl8/8fmlewFP74v6T5eMPJwMcx/FG
o/G80+ncCQQCu+FweMAY++n1eq/4fD7Lo6vW4bs7FzHY3MD4fRl7+dt4E9aOXlhUt6ZHDlXJyis/NjcA
2QOsngTWl/AlsYySmd+eZefquVk1Hr8tYlr9mBrkz7JzVbHyH3YfXwdiagxXOPRWOHRu8Cjb+I+z7Fxt
+U49eH3t7K9Pq0v4HF2AEjiCqks1KlmP3p9lD9WW73SkYuF36Nv0o7/lP1AsGrGjxCTVAAAAAElFTkSu
QmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRcS0S0FyFYHgOH4g
fgQXQl0NRIr1SqB9zGh34XAXs7gwgRfMVUILsY22mhmlDHTcRGQrbXVnkCKkSIOmtMk88QyMmDj/QQde
eDjndw4vx/3XWHa7/Qxj7IEkSd1sNgtRFNuMsSuMMa0oiip5lBFD7D9lnU53yu12y6lUCp1OB6qqolqt
IplM7oqiWGu1WgPy6vU6iCGWOuMBo9F4LRqNfms2m91AILDldDqHoVAIiqIgkUiAbvIoy+VybWKpMzkg
S5LUVxQl7PF45k0m0/dKpYJGo4FarYZCoQDyKCOGWOqMBwwGw+1wONxLp9P3BUEoWyyWA4fDAUEQDh/d
5FFGDLHUGQ/o9fpLXq/3vSzLvXw+/7VYLCKTySAWiyEejx/epXWGV9cv/Hm5NIfnjvmDp4unH40HOI7j
zWbzZZfLtRcMBvcjkUifMfbL5/Mt+/1+6+Nl6+Ddvavob25g1Crhx7O7eBPRDV9YNXcmR47Vto1Xf25u
AIoXWD0LrC/gi7yILQu/O83OVHlJMxq9LWJSvaQW5E+zM1W28R/2n9wEkloMVjh0Vzi0b/Eo2fmP0+xM
7fjPrb2+cfH3p9UFfE7MQQ2eQMWlGW7bTj6cZo/Vjv98vGzl9+jb9KOj8l/xBxqBzigbjwAAAABJRU5E
rkJggg==
</value>
</data>
<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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKbSURBVDhPhZBdSFNhHMbPZURXEXQTFF2trgKpdWFZWH6c
0vmxqUu3+bUskyyMUDqm4pxk5YwsmV1MsM3M6FA0NN1mzo+Dbec9y0ihwvmeQStGpO+xguDwxjvdzXHR
A+/Ny/N7/s//T1GbCrjK1PyQkQVDphj/pFIOusrltw5DjOsvZrneQnXCl1SCy2AWXBVR/rERBAbOMUGH
XjVrL1Bx/VqGs+vAbF9x1G/LMyu5uMjkODxYauXD6ABYQVoApXpBlOpCcC1nAa7u9/dorNO2/KjvNr21
Ce8ysvygEQTDSAUgugggahJEdB2I69cEKF0VRKlUCP/YN9WtAd5OmlXyFHCZYoGBEiYUQQUAosYNWGrY
hOuAiGoBlE55O2jG05kTU/IUP1QuB+16lSBKlxJTEyuQRgJENaTFWEuGyteVIyt5KuA0yeRgxEhAsCJd
JmE8RBfifyKqDkGpmAR4rfTWAM5RGuMeahleXM/aaIFq43AEmYGIqgS4Vs6L6PjojdPMWHPG1hXm+rTs
XK8OkEMJUCraBKsFca2CwAAizbvIzz2jbdm/3Y3p80qe8tty1TMPCqNvujVWfvnXXiEinSSVBSjpQhCl
Eniqp/DPwkg99jWcwM/rU/XKDGqyK9fst2mi3ltnwER7JkP2JW+0KZ2ZvJMnL75qwpGgHS/P3MWedg1+
UaXercygfFZaPW6h2fEOOkaO5bFky+6bmasTbWfxp8lO/PXDIP625MQfJ1rxSOUx2ZUsJJnYK2mGqe4S
HJ614ej7AbzC3cfzjyqwNUP1+eX5lO1Kf1I9q001eCz5eMndjBfdLXj6XhEeqTn8/anu4A6l958aNh8x
OMtS8OvWLDxcqf7i1KfsUnr+q+Gqozv7Sw5ZHKa0bYm/v22YpTjaxhhxAAAAAElFTkSuQmCC
</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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tngKj9WBZmDqX
em1t6tJtDV2WSRZGKN1ScV7JyhVZMnuYIJua0aXQ1Oamzj/DtnvuMlKocJ47aMWI9FwrCC4njrqX66Iv
nJfD9/P9fX8/itpUyFOm5nvNHOi1JMJ9VjnkschzLlMi2FXMBTvOqpO+lBI8Jpvgtsb5HjMIdZ9jwi6j
asapUwW79EzQaQDTncXxgKPQpuTWRSZvwKUsH0UHwDLSAyjVCKJUHYGr+fNwZX/gAc1OOQrj/rvarU14
j5kjk8NRpAIQXQIQ1QsiugHEtesClK4JolQqRH/sm2ynga9Vyyl5CngsiVB3CROJIR2AqG4Dlmo34Wog
oioApVO+Fi3jZfMTSp4K91rksNOoEkTpcnJqcgXSSICokrQYachW+dvyZCVPvXWbZXIwYiQgWJaukDAe
oovrfyKqiECpmAT42NytAbOu0kTwiZ7hxTXNRgtUtQ7HkA2IqFyAq+d5ER0fvpnFjDDZW1eY7dRzsx0G
QA4lQKloE6wQxFUrgQFE9LvYzz2vm3J/D9Vlzil5KuAoUE8/1sUn2mmWX/q1V4hJJ0llAUqGCETpBJ5w
6P7MD9Rgf+0J/KIm3ajMoMbbCmwBBx333TkNvM05DNmXvOH6TGb8Hi0vDNbjWNiJl6bv47FmGr8sV+9W
ZlB+Vqt+Y9dyoy25CXKsMbtGHryds+JtysOfxlvx1w89+NuiG3/0NuIB6zHZkyoklbirGabJ9hIcnXHg
+PtuvBx8hOeeWjGbpfr86kLadqU/pZ5XpZvG7Gfw4tAtvDDUgKceFuGBysPfnxkO7lB6/6l+2xGTuywN
jzZqcJ9V/cVtTNul9PxX/eVHd3aVHLK7LBnbkn9/ARsspRTlWFT8AAAAAElFTkSuQmCC
</value>
</data>
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@@ -386,6 +383,21 @@
8maMpXiUaCpB7aVH7Drag7TDOBxajYut0FpaD0XSzP7jfWfG6eiPsZKIYLLto83zJe2eGky2UlaTi9Rd
CvLG4etIrxoHvigqHxh+pXIo+nLlIEWVA6KoYkCUnXUy+Ff+FYK4Nr4lkOwikO7CtWEB5mjqCvLm5zfY
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>
</data>
</root>

View File

@@ -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 => "빈 공간을 클릭하면 캔버스 전체 속성을 편집할 수 있습니다.";
}
}
}

View File

@@ -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">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -73,6 +73,7 @@
<DependentUpon>UnifiedAGVCanvas.cs</DependentUpon>
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Models\AGVCommand.cs" />
<Compile Include="Models\Enums.cs" />
<Compile Include="Models\IMovableAGV.cs" />
<Compile Include="Models\VirtualAGV.cs" />

View File

@@ -83,6 +83,75 @@ namespace AGVNavigationCore.Controls
// UI 정보 그리기 (변환 없이)
if (_showGrid)
DrawUIInfo(g);
// 동기화 화면 그리기 (변환 없이, 최상위)
if (_canvasMode == CanvasMode.Sync)
{
DrawSyncScreen(g);
}
}
private void DrawSyncScreen(Graphics g)
{
// 반투명 검은색 배경
using (var brush = new SolidBrush(Color.FromArgb(200, 0, 0, 0)))
{
g.FillRectangle(brush, this.ClientRectangle);
}
// 중앙에 메시지 표시
var center = new Point(Width / 2, Height / 2);
// 메시지 폰트
using (var fontTitle = new Font("Malgun Gothic", 24, FontStyle.Bold))
using (var fontDetail = new Font("Malgun Gothic", 14))
using (var brushText = new SolidBrush(Color.White))
{
// 메인 메시지
var sizeTitle = g.MeasureString(_syncMessage, fontTitle);
g.DrawString(_syncMessage, fontTitle, brushText,
center.X - sizeTitle.Width / 2,
center.Y - sizeTitle.Height / 2 - 60);
// 진행률 바 배경
int barWidth = 500;
int barHeight = 30;
int barX = center.X - barWidth / 2;
int barY = center.Y + 10;
using (var brushBarBg = new SolidBrush(Color.FromArgb(64, 64, 64)))
{
g.FillRectangle(brushBarBg, barX, barY, barWidth, barHeight);
}
g.DrawRectangle(Pens.Gray, barX, barY, barWidth, barHeight);
// 진행률 바 채우기
if (_syncProgress > 0)
{
using (var brushProgress = new SolidBrush(Color.LimeGreen))
{
int fillWidth = (int)((barWidth - 4) * _syncProgress);
if (fillWidth > 0)
g.FillRectangle(brushProgress, barX + 2, barY + 2, fillWidth, barHeight - 4);
}
}
// 진행률 텍스트
string progressText = $"{(_syncProgress * 100):F0}%";
var sizeProgress = g.MeasureString(progressText, fontDetail);
g.DrawString(progressText, fontDetail, brushText,
center.X - sizeProgress.Width / 2,
barY + 5);
// 상세 메시지
if (!string.IsNullOrEmpty(_syncDetail))
{
var sizeDetail = g.MeasureString(_syncDetail, fontDetail);
g.DrawString(_syncDetail, fontDetail, brushText,
center.X - sizeDetail.Width / 2,
barY + barHeight + 20);
}
}
}
private void DrawGrid(Graphics g)
@@ -416,10 +485,10 @@ namespace AGVNavigationCore.Controls
case NodeType.Normal:
ghostBrush = new SolidBrush(Color.FromArgb(120, 100, 149, 237)); // 반투명 파란색
break;
case NodeType.Rotation:
ghostBrush = new SolidBrush(Color.FromArgb(120, 255, 165, 0)); // 반투명 주황색
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
ghostBrush = new SolidBrush(Color.FromArgb(120, 50, 205, 50)); // 반투명 초록색
break;
case NodeType.Charging:
@@ -439,7 +508,10 @@ namespace AGVNavigationCore.Controls
case NodeType.Image:
DrawImageGhost(g);
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
DrawPentagonGhost(g, ghostBrush);
break;
case NodeType.Charging:
@@ -646,7 +718,10 @@ namespace AGVNavigationCore.Controls
switch (node.Type)
{
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
DrawPentagonNodeShape(g, node, brush);
break;
case NodeType.Charging:
@@ -723,6 +798,13 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==true)
{
var crossRect = new Rectangle(rect.X - 3, rect.Y - 3, rect.Width + 6, rect.Height + 6);
g.DrawEllipse(new Pen(Color.DeepSkyBlue, 3), crossRect);
}
}
private void DrawPentagonNodeShape(Graphics g, MapNode node, Brush brush)
@@ -826,6 +908,21 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==false)
{
var crossPoints = new Point[5];
for (int i = 0; i < 5; i++)
{
var angle = (Math.PI * 2 * i / 5) - Math.PI / 2;
crossPoints[i] = new Point(
(int)(center.X + (radius + 4) * Math.Cos(angle)),
(int)(center.Y + (radius + 4) * Math.Sin(angle))
);
}
g.DrawPolygon(new Pen(Color.Gold, 3), crossPoints);
}
}
private void DrawTriangleNodeShape(Graphics g, MapNode node, Brush brush)
@@ -929,6 +1026,21 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==false)
{
var crossPoints = new Point[3];
for (int i = 0; i < 3; i++)
{
var angle = (Math.PI * 2 * i / 3) - Math.PI / 2;
crossPoints[i] = new Point(
(int)(center.X + (radius + 4) * Math.Cos(angle)),
(int)(center.Y + (radius + 4) * Math.Sin(angle))
);
}
g.DrawPolygon(new Pen(Color.Gold, 3), crossPoints);
}
}
private void DrawNodeLabel(Graphics g, MapNode node)
@@ -1663,6 +1775,11 @@ namespace AGVNavigationCore.Controls
/// </summary>
private Color GetAGVCenterColor(AGVState state)
{
// Stop-Mark 상태 (Error 상태와 유사하게 처리하거나 별도 처리)
// 여기서는 AGVState에 StopMark가 없으므로, 외부에서 상태를 Error로 설정하거나
// 별도의 플래그를 확인해야 함. 하지만 IAGV 인터페이스에는 플래그가 없음.
// 따라서 fMain에서 상태를 Error로 설정하는 것을 권장.
switch (state)
{
case AGVState.Moving: return Color.White;
@@ -1682,7 +1799,7 @@ namespace AGVNavigationCore.Controls
{
case AGVState.Moving: return Color.DarkGreen;
case AGVState.Charging: return Color.DarkBlue;
case AGVState.Error: return Color.DarkRed;
case AGVState.Error: return Color.DarkRed; // Stop-Mark 시 Error 상태 사용
case AGVState.Docking: return Color.DarkOrange;
default: return Color.DarkGray;
}

View File

@@ -17,6 +17,16 @@ namespace AGVNavigationCore.Controls
var worldPoint = ScreenToWorld(e.Location);
var hitNode = GetNodeAt(worldPoint);
// 에뮬레이터 모드 처리
if (_canvasMode == CanvasMode.Emulator)
{
if (e.Button == MouseButtons.Right && hitNode != null)
{
NodeRightClicked?.Invoke(this, hitNode);
}
return;
}
// 🔥 어떤 모드에서든 노드/빈 공간 클릭 시 선택 이벤트 발생 (속성창 업데이트)
bool ctrlPressed = (ModifierKeys & Keys.Control) == Keys.Control;
@@ -108,8 +118,10 @@ namespace AGVNavigationCore.Controls
switch (hitNode.Type)
{
case NodeType.Normal:
case NodeType.Rotation:
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Charging:
HandleNormalNodeDoubleClick(hitNode);
break;
@@ -429,7 +441,10 @@ namespace AGVNavigationCore.Controls
{
switch (node.Type)
{
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
return IsPointInPentagon(point, node);
case NodeType.Charging:
return IsPointInTriangle(point, node);

View File

@@ -35,7 +35,9 @@ namespace AGVNavigationCore.Controls
/// </summary>
public enum CanvasMode
{
Edit // 편집 가능 (맵 에디터)
Edit, // 편집 가능 (맵 에디터)
Sync, // 동기화 모드 (장비 설정 동기화)
Emulator // 에뮬레이터 모드
}
/// <summary>
@@ -116,6 +118,11 @@ namespace AGVNavigationCore.Controls
// RFID 중복 검사
private HashSet<string> _duplicateRfidNodes = new HashSet<string>();
// 동기화 모드 관련
private string _syncMessage = "동기화 중...";
private float _syncProgress = 0.0f;
private string _syncDetail = "";
// 브러쉬 및 펜
private Brush _normalNodeBrush;
private Brush _rotationNodeBrush;
@@ -140,6 +147,9 @@ namespace AGVNavigationCore.Controls
// 컨텍스트 메뉴
private ContextMenuStrip _contextMenu;
// 이벤트
public event EventHandler<MapNode> NodeRightClicked;
#endregion
#region Events
@@ -157,6 +167,7 @@ namespace AGVNavigationCore.Controls
#region Properties
public string PredictMessage { get; set; } = "";
public string MapFileName { get; set; } = "";
/// <summary>
@@ -546,6 +557,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
#region Cleanup

View 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})");
}
}
}

View File

@@ -2,7 +2,7 @@ using System;
namespace AGVNavigationCore.Models
{
/// <summary>
/// 노드 타입 열거형
@@ -11,10 +11,20 @@ namespace AGVNavigationCore.Models
{
/// <summary>일반 경로 노드</summary>
Normal,
/// <summary>회전 가능 지점</summary>
Rotation,
/// <summary>도킹 스테이션</summary>
Docking,
/// <summary>로더</summary>
Loader,
/// <summary>
/// 언로더
/// </summary>
UnLoader,
/// <summary>
/// 클리너
/// </summary>
Clearner,
/// <summary>
/// 버퍼
/// </summary>
Buffer,
/// <summary>충전 스테이션</summary>
Charging,
/// <summary>라벨 (UI 요소)</summary>
@@ -58,6 +68,10 @@ namespace AGVNavigationCore.Models
/// </summary>
public enum StationType
{
/// <summary>
/// 일반노드
/// </summary>
Node,
/// <summary>로더</summary>
Loader,
/// <summary>클리너</summary>
@@ -70,6 +84,24 @@ namespace AGVNavigationCore.Models
Charger
}
/// <summary>
/// AGV턴상태
/// </summary>
public enum AGVTurn
{
None=0,
/// <summary>
/// left turn 90"
/// </summary>
L90,
/// <summary>
/// right turn 90"
/// </summary>
R90
}
/// <summary>
/// 모터 명령 열거형 (실제 AGV 제어용)
/// </summary>
@@ -109,50 +141,42 @@ namespace AGVNavigationCore.Models
H
}
/// <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 Reason { get; set; }
public enum eAGVCommandReason
{
/// <summary>
/// 초기 미지정
/// </summary>
Normal,
/// <summary>
/// 생성자
/// 위치 미확정
/// </summary>
public AGVCommand(MotorCommand motor, MagnetPosition magnet, SpeedLevel speed, string reason = "")
{
Motor = motor;
Magnet = magnet;
Speed = speed;
Reason = reason;
}
UnknownPosition,
/// <summary>
/// 기본 생성자
/// 대상경로없음
/// </summary>
public AGVCommand()
{
Motor = MotorCommand.Stop;
Magnet = MagnetPosition.S;
Speed = SpeedLevel.L;
Reason = "";
}
NoTarget,
/// <summary>
/// 경로없음
/// </summary>
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})");
}
}
}

View File

@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Models
/// <summary>
/// 현재 노드 ID 조회
/// </summary>
string GetCurrentNodeId();
MapNode GetCurrentNode();
/// <summary>
/// AGV 상태 정보 문자열 조회

View File

@@ -226,7 +226,10 @@ namespace AGVNavigationCore.Models
case NodeType.Charging:
node.DockDirection = DockingDirection.Forward;
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
node.DockDirection = DockingDirection.Backward;
break;
default:

View File

@@ -34,6 +34,19 @@ namespace AGVNavigationCore.Models
/// </summary>
public NodeType Type { get; set; } = NodeType.Normal;
public bool CanDocking
{
get
{
if (Type == NodeType.Buffer) return true;
if (Type == NodeType.Loader) return true;
if (Type == NodeType.UnLoader) return true;
if (Type == NodeType.Clearner) return true;
if (Type == NodeType.Charging) return true;
return false;
}
}
/// <summary>
/// 도킹 방향 (도킹/충전 노드인 경우에만 Forward/Backward, 일반 노드는 DontCare)
/// </summary>
@@ -53,18 +66,38 @@ namespace AGVNavigationCore.Models
/// <summary>
/// 회전 가능 여부 (180도 회전 가능한 지점)
/// </summary>
public bool CanRotate { get; set; } = false;
public bool CanTurnLeft { get; set; } = true;
/// <summary>
/// 회전 가능 여부 (180도 회전 가능한 지점)
/// </summary>
public bool CanTurnRight { get; set; } = true;
/// <summary>
/// 교차로로 이용가능한지
/// </summary>
public bool DisableCross
{
get
{
if (Type != NodeType.Normal) return true;
return _disablecross;
}
set { _disablecross = value; }
}
private bool _disablecross = false;
/// <summary>
/// 장비 ID (도킹/충전 스테이션인 경우)
/// 예: "LOADER1", "CLEANER1", "BUFFER1", "CHARGER1"
/// </summary>
public string StationId { get; set; } = string.Empty;
public string NodeAlias { get; set; } = string.Empty;
/// <summary>
/// 장비 타입 (도킹/충전 스테이션인 경우)
/// </summary>
public StationType? StationType { get; set; } = null;
// public StationType? StationType { get; set; } = null;
/// <summary>
/// 노드 생성 일자
@@ -224,7 +257,7 @@ namespace AGVNavigationCore.Models
Type = type;
CreatedDate = DateTime.Now;
ModifiedDate = DateTime.Now;
// 타입별 기본 색상 설정
SetDefaultColorByType(type);
}
@@ -240,10 +273,10 @@ namespace AGVNavigationCore.Models
case NodeType.Normal:
DisplayColor = Color.Blue;
break;
case NodeType.Rotation:
DisplayColor = Color.Orange;
break;
case NodeType.Docking:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Loader:
DisplayColor = Color.Green;
break;
case NodeType.Charging:
@@ -283,21 +316,20 @@ 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>
///// <param name="stationType">장비 타입</param>
///// <param name="dockDirection">도킹 방향</param>
//public void SetDockingStation(string stationId, StationType stationType, DockingDirection dockDirection)
//{
// Type = NodeType.Docking;
// NodeAlias = stationId;
// DockDirection = dockDirection;
// SetDefaultColorByType(NodeType.Docking);
// ModifiedDate = DateTime.Now;
//}
/// <summary>
/// 충전 스테이션 설정
@@ -306,8 +338,7 @@ namespace AGVNavigationCore.Models
public void SetChargingStation(string stationId)
{
Type = NodeType.Charging;
StationId = stationId;
StationType = Models.StationType.Charger;
NodeAlias = stationId;
DockDirection = DockingDirection.Forward; // 충전기는 항상 전진 도킹
SetDefaultColorByType(NodeType.Charging);
ModifiedDate = DateTime.Now;
@@ -329,17 +360,17 @@ namespace AGVNavigationCore.Models
get
{
var displayText = NodeId;
if (!string.IsNullOrEmpty(Name))
{
displayText += $" - {Name}";
}
if (!string.IsNullOrEmpty(RfidId))
{
displayText += $" - [{RfidId}]";
}
return displayText;
}
}
@@ -358,9 +389,12 @@ namespace AGVNavigationCore.Models
Type = Type,
DockDirection = DockDirection,
ConnectedNodes = new List<string>(ConnectedNodes),
CanRotate = CanRotate,
StationId = StationId,
StationType = StationType,
CanTurnLeft = CanTurnLeft,
CanTurnRight = CanTurnRight,
DisableCross = DisableCross,
NodeAlias = NodeAlias,
CreatedDate = CreatedDate,
ModifiedDate = ModifiedDate,
IsActive = IsActive,
@@ -475,7 +509,7 @@ namespace AGVNavigationCore.Models
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)

View File

@@ -43,6 +43,8 @@ namespace AGVNavigationCore.Models
/// </summary>
public event EventHandler<string> ErrorOccurred;
#endregion
#region Fields
@@ -61,12 +63,12 @@ namespace AGVNavigationCore.Models
private int _currentNodeIndex;
private MapNode _currentNode;
private MapNode _prevNode;
private AGVTurn _turn;
// 이동 관련
private DateTime _lastUpdateTime;
private Point _moveStartPosition;
private Point _moveTargetPosition;
private float _moveProgress;
// 도킹 관련
private DockingDirection _dockingDirection;
@@ -79,17 +81,25 @@ namespace AGVNavigationCore.Models
private List<string> _detectedRfids = new List<string>(); // 감지된 RFID 목록
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
#region Properties
public bool Turn180 { get; set; } = false;
/// <summary>
/// 대상 이동시 모터 방향
/// </summary>
public AgvDirection PrevDirection => _prevDirection;
/// <summary>
/// AGV ID
/// </summary>
@@ -158,6 +168,11 @@ namespace AGVNavigationCore.Models
/// </summary>
public MapNode PrevNode => _prevNode;
/// <summary>
/// Turn 상태값
/// </summary>
public AGVTurn Turn { get; set; }
/// <summary>
/// 도킹 방향
/// </summary>
@@ -209,6 +224,8 @@ namespace AGVNavigationCore.Models
_currentPosition = position;
}
/// <summary>
/// 감지된 RFID 설정 (실제 RFID 센서에서)
/// </summary>
@@ -251,6 +268,19 @@ namespace AGVNavigationCore.Models
}
}
/// <summary>
/// 현재 노드id의 개체를 IsPass 로 설정합니다
/// </summary>
public bool SetCurrentNodeMarkStop()
{
if (_currentNode == 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>
/// 다음 동작 예측 (실제 AGV 제어용)
/// AGV가 지속적으로 호출하여 현재 상태와 예측 상태를 일치시킴
@@ -265,7 +295,8 @@ namespace AGVNavigationCore.Models
return new AGVCommand(
MotorCommand.Forward,
MagnetPosition.S, // 직진
SpeedLevel.L, // 저속
SpeedLevel.L, // 저속
eAGVCommandReason.UnknownPosition,
$"위치 미확정 (RFID {_detectedRfids.Count}/2) - 전진하여 RFID 탐색"
);
}
@@ -277,6 +308,7 @@ namespace AGVNavigationCore.Models
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
eAGVCommandReason.NoPath,
$"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.NodeId ?? ""}"
);
}
@@ -285,12 +317,32 @@ namespace AGVNavigationCore.Models
var lastNode = _currentPath.DetailedPath.Last();
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
{
return new AGVCommand(
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
$"목적지 도착 - 최종:{_currentNode?.NodeId ?? ""}"
);
// 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지)
if (_currentNode != null && _currentNode.NodeId == lastNode.NodeId)
{
if (lastNode.IsPass) //이미완료되었다.
{
return new AGVCommand(
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
eAGVCommandReason.Complete,
$"목적지 도착 - 최종:{_currentNode?.NodeId ?? ""}"
);
}
else
{
//마지막노드는 일혔지만 완료되지 않았다. 마크스탑필요
return new AGVCommand(
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
eAGVCommandReason.MarkStop,
$"목적지 도착 전(MarkStop) - 최종:{_currentNode?.NodeId ?? ""}"
);
}
}
}
// 4. 경로이탈
@@ -301,120 +353,14 @@ namespace AGVNavigationCore.Models
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
eAGVCommandReason.PathOut,
$"(재탐색요청)경로이탈 현재위치:{_currentNode.NodeId}"
);
}
// 5. 방향체크
//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
@@ -434,7 +380,7 @@ namespace AGVNavigationCore.Models
/// <summary>
/// 현재 노드 ID 조회
/// </summary>
public string GetCurrentNodeId() => _currentNode?.NodeId;
public MapNode GetCurrentNode() => _currentNode;
/// <summary>
/// AGV 정보 조회
@@ -494,6 +440,27 @@ namespace AGVNavigationCore.Models
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
#region Public Methods - ( )
@@ -527,10 +494,10 @@ namespace AGVNavigationCore.Models
_prevPosition = targetPosition;
_moveStartPosition = _currentPosition;
_moveTargetPosition = targetPosition;
_moveProgress = 0;
SetState(AGVState.Moving);
_isMoving = true;
Turn = AGVTurn.None;
}
/// <summary>
@@ -620,7 +587,6 @@ namespace AGVNavigationCore.Models
if (item != null)
{
//item.IsPass = true;
//이전노드는 모두 지나친걸로 한다
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
}
@@ -644,6 +610,19 @@ namespace AGVNavigationCore.Models
#endregion
/// <summary>
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
/// </summary>
public string GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId)
{
var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId);
return node?.HasRfid() == true ? node.RfidId : nodeId;
}
#region Private Methods
/// <summary>
@@ -662,6 +641,7 @@ namespace AGVNavigationCore.Models
defaultMotor,
MagnetPosition.S,
SpeedLevel.M,
eAGVCommandReason.NoPath,
$"{actionDescription} (DetailedPath 없음)"
);
}
@@ -681,6 +661,7 @@ namespace AGVNavigationCore.Models
defaultMotor,
MagnetPosition.S,
SpeedLevel.M,
eAGVCommandReason.NoTarget,
$"{actionDescription} (노드 {targetNodeId} 정보 없음)"
);
}
@@ -725,6 +706,7 @@ namespace AGVNavigationCore.Models
motorCmd,
magnetPos,
speed,
eAGVCommandReason.Normal,
$"{actionDescription} → {targetNodeId} (Motor:{motorCmd}, Magnet:{magnetPos})"
);
}
@@ -796,6 +778,29 @@ namespace AGVNavigationCore.Models
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()
{
if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
@@ -865,7 +870,10 @@ namespace AGVNavigationCore.Models
{
case NodeType.Charging:
return DockingDirection.Forward;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
return DockingDirection.Backward;
default:
return DockingDirection.Forward;
@@ -880,8 +888,6 @@ namespace AGVNavigationCore.Models
#endregion
#region Cleanup
/// <summary>

View File

@@ -101,7 +101,7 @@ namespace AGVNavigationCore.PathFinding.Core
/// <param name="startNodeId">시작 노드 ID</param>
/// <param name="endNodeId">목적지 노드 ID</param>
/// <returns>경로 계산 결과</returns>
public AGVPathResult FindPath(string startNodeId, string endNodeId)
public AGVPathResult FindPathAStar(string startNodeId, string endNodeId)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@@ -126,7 +126,6 @@ namespace AGVNavigationCore.PathFinding.Core
var startNode = _nodeMap[startNodeId];
var endNode = _nodeMap[endNodeId];
var openSet = new List<PathNode>();
var closedSet = new HashSet<string>();
var exploredCount = 0;
@@ -199,7 +198,7 @@ namespace AGVNavigationCore.PathFinding.Core
// 경유지가 없으면 기본 FindPath 호출
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)
{
return FindPath(startNodeId, endNodeId);
return FindPathAStar(startNodeId, endNodeId);
}
// 첫 번째 경유지가 시작노드와 같은지 검사
@@ -267,7 +266,7 @@ namespace AGVNavigationCore.PathFinding.Core
string waypoint = validWaypoints[i];
// 현재 위치에서 경유지까지의 경로 계산
var segmentResult = FindPath(currentStart, waypoint);
var segmentResult = FindPathAStar(currentStart, waypoint);
if (!segmentResult.Success)
{
@@ -295,7 +294,7 @@ namespace AGVNavigationCore.PathFinding.Core
}
// 2단계: 마지막 경유지에서 최종 목적지까지의 경로 계산
var finalSegmentResult = FindPath(currentStart, endNodeId);
var finalSegmentResult = FindPathAStar(currentStart, endNodeId);
if (!finalSegmentResult.Success)
{
@@ -442,7 +441,7 @@ namespace AGVNavigationCore.PathFinding.Core
AGVPathResult bestResult = null;
foreach (var targetId in targetNodeIds)
{
var result = FindPath(startNodeId, targetId);
var result = FindPathAStar(startNodeId, targetId);
if (result.Success && (bestResult == null || result.TotalDistance < bestResult.TotalDistance))
{
bestResult = result;

View File

@@ -22,8 +22,6 @@ namespace AGVNavigationCore.PathFinding.Planning
private readonly JunctionAnalyzer _junctionAnalyzer;
private readonly DirectionChangePlanner _directionChangePlanner;
public AGVPathfinder(List<MapNode> mapNodes)
{
_mapNodes = mapNodes ?? new List<MapNode>();
@@ -49,8 +47,9 @@ namespace AGVNavigationCore.PathFinding.Planning
n.IsActive &&
n.IsNavigationNode() &&
n.ConnectedNodes != null &&
n.DisableCross == false &&
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
).ToList();
@@ -99,9 +98,10 @@ namespace AGVNavigationCore.PathFinding.Planning
if (pathNode != null &&
pathNode.IsActive &&
pathNode.IsNavigationNode() &&
pathNode.DisableCross == false &&
pathNode.ConnectedNodes != null &&
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)
return pathNode;
@@ -122,12 +122,12 @@ namespace AGVNavigationCore.PathFinding.Planning
if (prevNode == null)
return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0);
if (startNode.NodeId == targetNode.NodeId && targetNode.DockDirection.MatchAGVDirection(prevDirection))
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode,startNode }, new List<AgvDirection>(), 0, 0);
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode, startNode }, new List<AgvDirection>(), 0, 0);
var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
//1.목적지까지의 최단거리 경로를 찾는다.
var pathResult = _basicPathfinder.FindPath(startNode.NodeId, targetNode.NodeId);
var pathResult = _basicPathfinder.FindPathAStar(startNode.NodeId, targetNode.NodeId);
pathResult.PrevNode = prevNode;
pathResult.PrevDirection = prevDirection;
if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0)
@@ -221,7 +221,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//진행방향으로 이동했을때 나오는 노드를 사용한다.
if (nextNodeForward != null)
{
var Path0 = _basicPathfinder.FindPath(startNode.NodeId, nextNodeForward.NodeId);
var Path0 = _basicPathfinder.FindPathAStar(startNode.NodeId, nextNodeForward.NodeId);
Path0.PrevNode = prevNode;
Path0.PrevDirection = prevDirection;
MakeDetailData(Path0, prevDirection);
@@ -267,7 +267,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//경유지를 포함하여 경로를 다시 계산한다.
//1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다)
var path1 = _basicPathfinder.FindPath(startNode.NodeId, JunctionInPath.NodeId);
var path1 = _basicPathfinder.FindPathAStar(startNode.NodeId, JunctionInPath.NodeId);
path1.PrevNode = prevNode;
path1.PrevDirection = prevDirection;
@@ -295,7 +295,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//2.교차로 - 종료위치
var path2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, targetNode.NodeId);
var path2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, targetNode.NodeId);
path2.PrevNode = prevNode;
path2.PrevDirection = prevDirection;
@@ -332,7 +332,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//if (tempNode != null)
{
// 교차로 → 대체노드 경로 계산
var pathToTemp = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode.NodeId);
var pathToTemp = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode.NodeId);
pathToTemp.PrevNode = JunctionInPath;
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathToTemp.Success)
@@ -348,7 +348,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
// 대체노드 → 교차로 경로 계산 (역방향)
var pathFromTemp = _basicPathfinder.FindPath(tempNode.NodeId, JunctionInPath.NodeId);
var pathFromTemp = _basicPathfinder.FindPathAStar(tempNode.NodeId, JunctionInPath.NodeId);
pathFromTemp.PrevNode = JunctionInPath;
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathFromTemp.Success)
@@ -362,7 +362,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다.
bool temp3ok = false;
var TempCheck3 = _basicPathfinder.FindPath(combinedResult.Path.Last().NodeId, targetNode.NodeId);
var TempCheck3 = _basicPathfinder.FindPathAStar(combinedResult.Path.Last().NodeId, targetNode.NodeId);
if (TempCheck3.Path.First().NodeId.Equals(combinedResult.Path.Last().NodeId))
{
if (targetNode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward)
@@ -385,7 +385,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult.Path[combinedResult.Path.Count - 2].NodeId,
path2.Path[1].NodeId);
var pathToTemp2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode2.NodeId);
var pathToTemp2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode2.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
else MakeDetailData(pathToTemp2, ReverseDirection);
@@ -400,7 +400,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
}
var pathToTemp3 = _basicPathfinder.FindPath(tempNode2.NodeId, JunctionInPath.NodeId);
var pathToTemp3 = _basicPathfinder.FindPathAStar(tempNode2.NodeId, JunctionInPath.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
else MakeDetailData(pathToTemp3, currentDirection);
@@ -438,7 +438,7 @@ namespace AGVNavigationCore.PathFinding.Planning
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
// 노드 정보 생성 (현재 방향 유지)
var nodeInfo = new NodeMotorInfo(i+1,
var nodeInfo = new NodeMotorInfo(i + 1,
nodeId, RfidId,
currentDirection,
nextNodeId,

View File

@@ -73,7 +73,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 방향이 같으면 직접 경로 계산
if (currentDirection == requiredDirection)
{
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath.Success)
{
return DirectionChangePlan.CreateSuccess(
@@ -85,7 +85,7 @@ namespace AGVNavigationCore.PathFinding.Planning
}
// 방향 전환이 필요한 경우 - 먼저 간단한 직접 경로 확인
var directPath2 = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath2 = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath2.Success)
{
// 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환
@@ -160,7 +160,7 @@ namespace AGVNavigationCore.PathFinding.Planning
}
// 2. 직진 경로상의 갈림길들도 검색 (단, 되돌아가기 방지)
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath.Success)
{
foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외
@@ -261,7 +261,7 @@ namespace AGVNavigationCore.PathFinding.Planning
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;
distances.Add((junction, distance));
}
@@ -313,12 +313,12 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지의 경로
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
// 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.NodeId == junctionNodeId);
if (isNearbyDetour)
@@ -341,7 +341,7 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지 직진 (현재 방향 유지)
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
@@ -349,7 +349,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 2. 갈림길에서 방향 전환 후 목적지로
// 이때 마그넷 센서를 이용해 목적지 방향으로 진입
var fromJunctionPath = _pathfinder.FindPath(junctionNodeId, targetNodeId);
var fromJunctionPath = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
{
fullPath.AddRange(fromJunctionPath.Path.Skip(1)); // 중복 노드 제거
@@ -366,7 +366,7 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지의 경로
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
@@ -387,7 +387,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 3. 갈림길에서 목표점까지의 경로
string lastNode = fullPath.LastOrDefault()?.NodeId ?? junctionNodeId;
var fromJunctionPath = _pathfinder.FindPath(lastNode, targetNodeId);
var fromJunctionPath = _pathfinder.FindPathAStar(lastNode, targetNodeId);
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
{
fullPath.AddRange(fromJunctionPath.Path.Skip(1));
@@ -609,7 +609,7 @@ namespace AGVNavigationCore.PathFinding.Planning
private bool CanReachTargetViaJunction(string junctionNodeId, string targetNodeId)
{
// 갈림길에서 목적지까지의 경로가 존재하는지 확인
var pathToTarget = _pathfinder.FindPath(junctionNodeId, targetNodeId);
var pathToTarget = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
return pathToTarget.Success;
}

View File

@@ -153,7 +153,7 @@ namespace AGVNavigationCore.Utils
Console.WriteLine($"이름: {node.Name}");
Console.WriteLine($"위치: {node.Position}");
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($"연결된 노드:");

View File

@@ -15,6 +15,8 @@ using Newtonsoft.Json;
using AGVNavigationCore.PathFinding.Planning;
using AGVNavigationCore.PathFinding.Core;
using AGVSimulator.Models;
using System.IO.Ports;
using System.Text;
namespace AGVSimulator.Forms
{
@@ -25,6 +27,86 @@ namespace AGVSimulator.Forms
{
#region Fields
// Emulator Fields
private SerialPort _emulatorPort;
private ComboBox _portCombo;
private Button _connectButton;
private bool _isEmulatorConnected = false;
// Emulator State Fields
private UInt16 _emu_system0 = 0;
private UInt16 _emu_system1 = 0;
private UInt16 _emu_error = 0;
private byte _emu_signal = 0;
private char _emu_sts_bunki = 'S';
private char _emu_sts_speed = 'L';
private char _emu_sts_dir = 'F';
private char _emu_sts_sensor = '1';
private string _lastSentNodeId = null;
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,
front_detect_ignore,
front_detect_check,
stop_by_front_detect = 12,
stop_by_cross_in,
agv_stop,
agv_run
}
public enum eerror
{
Emergency = 0,
Overcurrent,
Charger_run_error,
Charger_pos_error,
line_out_error = 4,
runerror_by_no_magent_line,
controller_comm_error = 11,
arrive_ctl_comm_error,
door_ctl_comm_error,
charger_comm_error,
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 estsvaluetype
{
bunki,
speed,
direction,
sensor
}
private UnifiedAGVCanvas _simulatorCanvas;
private List<MapNode> _mapNodes;
private AGVPathfinder _advancedPathfinder;
@@ -92,6 +174,9 @@ namespace AGVSimulator.Forms
// 방향 콤보박스 초기화
InitializeDirectionCombo();
// 에뮬레이터 UI 초기화
InitializeEmulatorUI();
// 초기 상태 설정
UpdateUI();
@@ -338,6 +423,19 @@ namespace AGVSimulator.Forms
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes);
}
//마지막대상이 버퍼라면 시퀀스처리를 해야한다
if(targetNode.Type == NodeType.Buffer)
{
var lastDetailPath = advancedResult.DetailedPath.Last();
if(lastDetailPath.NodeId == targetNode.NodeId) //마지막노드 재확인
{
//버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다
advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList();
Console.WriteLine("최종위치가 버퍼이므로 마지막 RFID에서 멈추도록 합니다");
}
}
_simulatorCanvas.CurrentPath = advancedResult;
_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
@@ -703,12 +801,32 @@ namespace AGVSimulator.Forms
foreach (var agv in _agvList)
{
agv.Update(100); // 100ms 간격으로 업데이트
// Emulator Tag Logic
if (_isEmulatorConnected && agv == _agvList.FirstOrDefault())
{
if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
{
var rfid = GetRfidByNodeId(agv.CurrentNodeId);
if (!string.IsNullOrEmpty(rfid))
{
SendTag(rfid);
_lastSentNodeId = agv.CurrentNodeId;
}
}
}
}
}
// UI 업데이트
UpdateUI();
_simulatorCanvas.Invalidate(); // 화면 다시 그리기
// 에뮬레이터 상태 전송
if (_isEmulatorConnected)
{
SendEmulatorStatus();
}
}
#endregion
@@ -1443,7 +1561,7 @@ namespace AGVSimulator.Forms
// 도킹 타겟 노드 찾기
var dockingTargets = _mapNodes.Where(n =>
n.Type == NodeType.Charging || n.Type == NodeType.Docking).ToList();
n.Type == NodeType.Charging || n.Type == NodeType.Loader || n.Type == NodeType.UnLoader || n.Type == NodeType.Clearner || n.Type == NodeType.Buffer).ToList();
if (dockingTargets.Count == 0)
{
@@ -1790,7 +1908,7 @@ namespace AGVSimulator.Forms
$"모터: {command.Motor}\n" +
$"마그넷: {command.Magnet}\n" +
$"속도: {command.Speed}\n" +
$"이유: {command.Reason}\n\n" +
$"이유: {command.Message}\n\n" +
$"---\n" +
$"현재 상태: {agv.CurrentState}\n" +
$"현재 방향: {agv.CurrentDirection}\n" +
@@ -1811,7 +1929,7 @@ namespace AGVSimulator.Forms
// 첫 번째 AGV의 다음 행동 예측
var agv = _agvList[0];
var command = agv.Predict();
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Message}";
}
@@ -1992,6 +2110,412 @@ namespace AGVSimulator.Forms
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#region Emulator Logic
private void InitializeEmulatorUI()
{
// 에뮬레이터 제어 패널 생성
var emulatorPanel = new GroupBox();
emulatorPanel.Text = "AGV Emulator (RS232)";
emulatorPanel.Height = 60;
emulatorPanel.Dock = DockStyle.Top;
var layout = new FlowLayoutPanel();
layout.Dock = DockStyle.Fill;
layout.Padding = new Padding(5);
layout.AutoSize = true;
// 포트 선택 콤보박스
_portCombo = new ComboBox();
_portCombo.Width = 100;
_portCombo.DropDownStyle = ComboBoxStyle.DropDownList;
_portCombo.DropDown += (s, e) => {
_portCombo.Items.Clear();
_portCombo.Items.AddRange(SerialPort.GetPortNames());
};
_portCombo.Items.AddRange(SerialPort.GetPortNames());
if (_portCombo.Items.Count > 0) _portCombo.SelectedIndex = 0;
// 연결 버튼
_connectButton = new Button();
_connectButton.Text = "Connect";
_connectButton.Click += OnConnectEmulator_Click;
layout.Controls.Add(new Label { Text = "Port:", AutoSize = true, Margin = new Padding(3, 8, 3, 3) });
layout.Controls.Add(_portCombo);
layout.Controls.Add(_connectButton);
emulatorPanel.Controls.Add(layout);
// 폼의 최상단에 추가
this.Controls.Add(emulatorPanel);
emulatorPanel.BringToFront();
}
private void OnConnectEmulator_Click(object sender, EventArgs e)
{
if (_isEmulatorConnected)
{
DisconnectEmulator();
}
else
{
if (_portCombo.SelectedItem == null)
{
MessageBox.Show("포트를 선택해주세요.");
return;
}
ConnectEmulator(_portCombo.SelectedItem.ToString());
}
}
private void ConnectEmulator(string portName)
{
try
{
_emulatorPort = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One);
_emulatorPort.DataReceived += OnEmulatorDataReceived;
_emulatorPort.Open();
_isEmulatorConnected = true;
_connectButton.Text = "Disconnect";
_portCombo.Enabled = false;
MessageBox.Show($"에뮬레이터 시작: {portName}");
}
catch (Exception ex)
{
MessageBox.Show($"연결 실패: {ex.Message}");
}
}
private void DisconnectEmulator()
{
try
{
if (_emulatorPort != null && _emulatorPort.IsOpen)
{
_emulatorPort.Close();
}
_isEmulatorConnected = false;
_connectButton.Text = "Connect";
_portCombo.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show($"해제 실패: {ex.Message}");
}
}
private StringBuilder _recvBuffer = new StringBuilder();
private void OnEmulatorDataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string data = _emulatorPort.ReadExisting();
_recvBuffer.Append(data);
string buffer = _recvBuffer.ToString();
int stxIndex = buffer.IndexOf((char)0x02);
while (stxIndex >= 0)
{
int etxIndex = buffer.IndexOf((char)0x03, stxIndex);
if (etxIndex > stxIndex)
{
string packet = buffer.Substring(stxIndex + 1, etxIndex - stxIndex - 1);
ProcessEmulatorPacket(packet);
// 처리된 패킷 제거
buffer = buffer.Substring(etxIndex + 1);
stxIndex = buffer.IndexOf((char)0x02);
}
else
{
break; // ETX 아직 안옴
}
}
_recvBuffer.Clear();
_recvBuffer.Append(buffer);
}
catch (Exception ex)
{
Console.WriteLine($"Emulator Recv Error: {ex.Message}");
}
}
#region Emulator Helpers
private bool GetBit(ref UInt16 _value, int idx)
{
var offset = (UInt16)(1 << idx);
return (_value & offset) != 0;
}
private bool SetBit(ref UInt16 _value, int idx, bool 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;
}
private bool SetBit(ref byte _value, int idx, bool value)
{
var offset = (byte)(1 << idx);
if (value) _value |= offset;
else _value &= (byte)~offset;
return true;
}
private void SetAGV(esystemflag0 flag, bool value)
{
SetBit(ref _emu_system0, (int)flag, value);
}
private void SetAGV(esystemflag1 flag, bool value)
{
SetBit(ref _emu_system1, (int)flag, value);
}
private void SetAGV(eerror flag, bool value)
{
SetBit(ref _emu_error, (int)flag, value);
}
private void SetAGV(esignal flag, bool value)
{
SetBit(ref _emu_signal, (int)flag, value);
}
private void SetSTS(estsvaluetype target, char value)
{
switch (target)
{
case estsvaluetype.sensor: _emu_sts_sensor = value; break;
case estsvaluetype.direction: _emu_sts_dir = value; break;
case estsvaluetype.speed: _emu_sts_speed = value; break;
case estsvaluetype.bunki: _emu_sts_bunki = value; break;
}
}
#endregion
private void ProcessEmulatorPacket(string packet)
{
// Packet: CMD(3) + DATA(...) + Checksum(2)
// But here packet is substring between STX and ETX.
// Example: STS...Checksum
if (packet.Length < 3) return;
string cmd = packet.Substring(0, 3);
string data = "";
if (packet.Length > 5) // CMD + Checksum(2)
{
data = packet.Substring(3, packet.Length - 5);
}
// AGV 제어 (첫 번째 AGV 대상)
var agv = _agvList.FirstOrDefault();
this.Invoke(new Action(() => {
switch (cmd)
{
case "CRN": // 기동명령
if (data.Length > 0) SetSTS(estsvaluetype.direction, data[0]);
SetAGV(esystemflag1.agv_stop, false);
SetAGV(esystemflag1.agv_run, true);
if (agv != null) agv.Resume();
break;
case "CST": // 중지명령
if (data.StartsWith("M"))
{
// Mark Stop
// TODO: Implement Mark Stop logic in VirtualAGV if needed
// For now, just log it
Console.WriteLine("Mark Stop Command Received");
}
else
{
SetAGV(esystemflag1.agv_run, false);
SetAGV(esystemflag1.agv_stop, true);
if (agv != null) agv.Pause();
}
break;
case "CBR": // 분기명령
// FSL1
if (data.Length >= 4)
{
SetSTS(estsvaluetype.direction, data[0]);
SetSTS(estsvaluetype.bunki, data[1]);
SetSTS(estsvaluetype.speed, data[2]);
SetSTS(estsvaluetype.sensor, data[3]);
}
break;
case "CRT": // 수동제어
if (data.Length >= 4)
{
_emu_sts_dir = data[0];
_emu_sts_bunki = data[1];
_emu_sts_speed = data[2];
_emu_sts_sensor = data[3];
SetAGV(esystemflag1.agv_stop, false);
SetAGV(esystemflag1.agv_run, true);
if (agv != null) agv.Resume();
}
break;
case "SFR": // Reset
SetAGV(eerror.Emergency, false);
SetAGV(eerror.line_out_error, false);
SetAGV(eerror.Overcurrent, false);
SetAGV(esystemflag1.agv_run, false);
SetAGV(esystemflag1.agv_stop, true);
if (agv != null) agv.Pause();
break;
case "CBT": // 충전
SetAGV(esystemflag1.Battery_charging, true);
if (data.Length >= 5)
{
var cmdChar = data[4];
if (cmdChar == 'I') SetAGV(esystemflag1.Battery_charging, true);
else SetAGV(esystemflag1.Battery_charging, false);
}
break;
case "ACK":
// Log ACK
break;
default:
// Send ACK for other commands
SendCmd("ACK", cmd);
break;
}
}));
}
private void SendCmd(string cmd, string value)
{
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
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);
try
{
_emulatorPort.Write(barr.ToArray(), 0, barr.Count);
}
catch { }
}
public void SendTag(string tagno)
{
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
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);
try
{
_emulatorPort.Write(barr.ToArray(), 0, barr.Count);
}
catch { }
}
private void SendEmulatorStatus()
{
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
var agv = _agvList.FirstOrDefault();
// Sync state from VirtualAGV
if (agv != null)
{
// Update Battery
// Update Position/Tag?
}
// STS Packet Construction
// STS(3) + Volt(3) + Sys0(4) + Sys1(4) + Err(4) + Spd(1) + Bunki(1) + Dir(1) + Sensor(1) + Signal(2) + Checksum(2)
// Default buffer
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(' ').Select(t => Convert.ToByte(t, 16)).ToArray();
// Volt (255 for now)
var voltstr = "255";
var bufarr = System.Text.Encoding.Default.GetBytes(voltstr);
Array.Copy(bufarr, 0, barr, 4, bufarr.Length);
// System0
bufarr = System.Text.Encoding.Default.GetBytes(_emu_system0.ToString("X2").PadLeft(4, '0'));
Array.Copy(bufarr, 0, barr, 7, bufarr.Length);
// System1
bufarr = System.Text.Encoding.Default.GetBytes(_emu_system1.ToString("X2").PadLeft(4, '0'));
Array.Copy(bufarr, 0, barr, 11, bufarr.Length);
// Error
bufarr = System.Text.Encoding.Default.GetBytes(_emu_error.ToString("X2").PadLeft(4, '0'));
Array.Copy(bufarr, 0, barr, 15, bufarr.Length);
// Status Chars
barr[19] = (byte)_emu_sts_speed;
barr[20] = (byte)_emu_sts_bunki;
barr[21] = (byte)_emu_sts_dir;
barr[22] = (byte)_emu_sts_sensor;
// Signal
bufarr = System.Text.Encoding.Default.GetBytes(_emu_signal.ToString("X2").PadLeft(2, '0'));
Array.Copy(bufarr, 0, barr, 23, bufarr.Length);
// Checksum (**)
barr[barr.Length - 3] = (byte)'*';
barr[barr.Length - 2] = (byte)'*';
try
{
_emulatorPort.Write(barr, 0, barr.Length);
}
catch { }
}
private string CalculateChecksum(string data)
{
int sum = 0;
foreach (char c in data) sum += c;
// 16진수 변환 후 뒤 2자리
string hex = sum.ToString("X");
if (hex.Length >= 2) return hex.Substring(hex.Length - 2);
return hex.PadLeft(2, '0');
}
#endregion
}
/// <summary>

View File

@@ -5,15 +5,17 @@
"Name": "UNLOADER",
"Position": "65, 229",
"Type": 2,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N002"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Red",
"RfidId": "0001",
@@ -42,16 +44,18 @@
"Name": "N002",
"Position": "190, 230",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N003",
"N001"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0002",
@@ -80,16 +84,18 @@
"Name": "N003",
"Position": "296, 266",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N004",
"N002"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0003",
@@ -118,6 +124,7 @@
"Name": "N004",
"Position": "388, 330",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N022",
@@ -125,11 +132,12 @@
"N011",
"N003"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0004",
@@ -158,16 +166,18 @@
"Name": "N006",
"Position": "530, 220",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N007",
"N022"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0013",
@@ -196,16 +206,18 @@
"Name": "N007",
"Position": "589, 184",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N019",
"N006"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0014",
@@ -234,16 +246,18 @@
"Name": "N008",
"Position": "282, 452",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N009",
"N031"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0009",
@@ -272,16 +286,18 @@
"Name": "N009",
"Position": "183, 465",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N010",
"N008"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0010",
@@ -309,16 +325,18 @@
"NodeId": "N010",
"Name": "TOPS",
"Position": "52, 466",
"Type": 2,
"Type": 3,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N009"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Red",
"RfidId": "0011",
@@ -347,17 +365,19 @@
"Name": "N011",
"Position": "481, 399",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N012",
"N015",
"N004"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0005",
@@ -386,16 +406,18 @@
"Name": "N012",
"Position": "559, 464",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N013",
"N011"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0006",
@@ -424,16 +446,18 @@
"Name": "N013",
"Position": "640, 513",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N014",
"N012"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0007",
@@ -461,16 +485,18 @@
"NodeId": "N014",
"Name": "LOADER",
"Position": "728, 573",
"Type": 2,
"Type": 1,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N013"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Red",
"RfidId": "0008",
@@ -498,16 +524,18 @@
"NodeId": "N019",
"Name": "CHARGER #2",
"Position": "679, 199",
"Type": 3,
"Type": 5,
"CanDocking": true,
"DockDirection": 1,
"ConnectedNodes": [
"N007"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Magenta",
"RfidId": "0015",
@@ -536,17 +564,19 @@
"Name": "N022",
"Position": "461, 267",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N023",
"N004",
"N006"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0012",
@@ -575,16 +605,18 @@
"Name": "N023",
"Position": "418, 206",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N024",
"N022"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0016",
@@ -613,16 +645,18 @@
"Name": "N024",
"Position": "476, 141",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N025",
"N023"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0017",
@@ -651,16 +685,18 @@
"Name": "N025",
"Position": "548, 99",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N026",
"N024"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0018",
@@ -688,16 +724,18 @@
"NodeId": "N026",
"Name": "CHARGER #1",
"Position": "670, 88",
"Type": 3,
"Type": 5,
"CanDocking": true,
"DockDirection": 1,
"ConnectedNodes": [
"N025"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Magenta",
"RfidId": "0019",
@@ -725,14 +763,16 @@
"NodeId": "LBL001",
"Name": "Amkor Technology Korea",
"Position": "183, 103",
"Type": 4,
"DockDirection": 0,
"Type": 6,
"CanDocking": false,
"DockDirection": 2,
"ConnectedNodes": [],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Purple",
"RfidId": "",
@@ -760,14 +800,16 @@
"NodeId": "IMG001",
"Name": "logo",
"Position": "633, 310",
"Type": 5,
"DockDirection": 0,
"Type": 7,
"CanDocking": false,
"DockDirection": 1,
"ConnectedNodes": [],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Brown",
"RfidId": "",
@@ -796,16 +838,18 @@
"Name": "",
"Position": "448, 476",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N016",
"N011"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0037",
@@ -834,16 +878,18 @@
"Name": "",
"Position": "425, 524",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N017",
"N015"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0036",
@@ -872,16 +918,18 @@
"Name": "",
"Position": "389, 559",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N018",
"N016"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0035",
@@ -910,17 +958,19 @@
"Name": "",
"Position": "315, 562",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N030",
"N017",
"N005"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0034",
@@ -949,17 +999,19 @@
"Name": "",
"Position": "227, 560",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N020",
"N029",
"N018"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0033",
@@ -988,17 +1040,19 @@
"Name": "",
"Position": "142, 557",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N021",
"N028",
"N005"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0032",
@@ -1027,16 +1081,18 @@
"Name": "",
"Position": "60, 559",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N027",
"N020"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0031",
@@ -1064,16 +1120,18 @@
"NodeId": "N027",
"Name": "BUF1",
"Position": "61, 645",
"Type": 2,
"Type": 4,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N021"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Green",
"RfidId": "0041",
@@ -1101,16 +1159,18 @@
"NodeId": "N028",
"Name": "BUF2",
"Position": "141, 643",
"Type": 2,
"Type": 4,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N020"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Green",
"RfidId": "0040",
@@ -1138,16 +1198,18 @@
"NodeId": "N029",
"Name": "BUF3",
"Position": "229, 638",
"Type": 2,
"Type": 4,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N005"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Green",
"RfidId": "0039",
@@ -1175,16 +1237,18 @@
"NodeId": "N030",
"Name": "BUF4",
"Position": "316, 638",
"Type": 2,
"Type": 4,
"CanDocking": true,
"DockDirection": 2,
"ConnectedNodes": [
"N018"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": true,
"NodeAlias": "",
"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,
"DisplayColor": "Green",
"RfidId": "0038",
@@ -1213,16 +1277,18 @@
"Name": "",
"Position": "337, 397",
"Type": 0,
"CanDocking": false,
"DockDirection": 0,
"ConnectedNodes": [
"N004",
"N008"
],
"CanRotate": false,
"StationId": "",
"StationType": null,
"CanTurnLeft": true,
"CanTurnRight": true,
"DisableCross": false,
"NodeAlias": "",
"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,
"DisplayColor": "Cyan",
"RfidId": "0030",
@@ -1251,6 +1317,6 @@
"BackgroundColorArgb": -14671840,
"ShowGrid": false
},
"CreatedDate": "2025-12-08T11:55:04.9060276+09:00",
"CreatedDate": "2025-12-09T17:00:29.7458276+09:00",
"Version": "1.1"
}

View File

@@ -99,7 +99,7 @@
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</PropertyGroup>
<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>
<HintPath>..\DLL\arCommUtil.dll</HintPath>
</Reference>
@@ -284,6 +284,33 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PUB.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">
<SubType>Form</SubType>
</Compile>
@@ -305,12 +332,6 @@
<Compile Include="StateMachine\Step\_SM_RUN_GOHOME.cs">
<SubType>Form</SubType>
</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">
<SubType>Form</SubType>
</Compile>

View File

@@ -246,23 +246,18 @@ namespace Project
public bool AutoModeOffAndClearPosition { get; set; }
[Browsable(false)]
public UInt16 GDSValue { get; set; }
[Browsable(false)]
public string musicfile { get; set; }
/// <summary>
/// FVI로 가능 방향이 Backward 인가?
/// Forward 방향이라면 false 를 한다
/// </summary>
[Browsable(false)]
public Boolean AGV_Direction_FVI_Backward { get; set; }
[Browsable(false)]
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)]
public string AGV_PANID { get; set; }
@@ -328,7 +323,12 @@ namespace Project
public Single interval_xbe { get; set; }
[Browsable(false)]
public int interval_bms { get; set; }
public int doorSoundTerm { get; set; }
/// <summary>
/// 스피커출력 간격
/// </summary>
[DisplayName("Speak Term")]
public int alarmSoundTerm { get; set; }
[Browsable(false)]
public int musicvol { get; set; }
@@ -336,6 +336,16 @@ namespace Project
public bool Enable_Music { get; set; }
#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)]
public string LastMapFile { get; set; }
@@ -366,6 +376,7 @@ namespace Project
if (StatusInterval < 10) StatusInterval = 300; //5분간격
if (XBE_ID < 1) XBE_ID = 0xFF; //0은 acs 이므로 쓰지못하게한다.
if (SAD == 0) SAD = 999;
if (GDSValue == 0) GDSValue = 1000;
if (TAGF1A == 0)
{
TAGNOT = 9000;
@@ -418,7 +429,7 @@ namespace Project
//충전은 10분간격으로 재시도 한다
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
if (doorSoundTerm == 0) doorSoundTerm = 15; //기본 15초
if (alarmSoundTerm == 0) alarmSoundTerm = 15; //기본 15초
if (ChargeSearchTime == 0) ChargeSearchTime = 25;
//최대 충전진행 시간(기본 1시간)
if (ChargeMaxTime == 0) ChargeMaxTime = 3600;

View File

@@ -67,8 +67,7 @@ namespace Project
/// </summary>
private ePosition _targetPos = ePosition.NONE;
public event EventHandler TargetPosSet;
public string result_message = "";
public double result_progressmax = 0;
public double result_progressvalue = 0;
@@ -91,8 +90,6 @@ namespace Project
set
{
_targetPos = value;
TargetPosSet?.Invoke(this, null);
PUB.log.Add(string.Format("대상위치설정:{0}", value));
}
}

View File

@@ -51,8 +51,7 @@ namespace Project
public static string AGV연결실패 { get { return string.Format("AGV {0}", ); } }
public static string = "이동 예측이 동작하지 않습니다. 개발부서에 문의 하세요";
public static string = "목적지 이동 이 완료 되었습니다";
public static string = "홈검색을시작합니다";
public static string FindCurrentPisition = "현재 위치를 검색 합니다";
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 QC이동버튼은상하차에서만사용가능합니다 = "QC이동 버튼은 상,하차 에서만 사용 가능합니다";
public static string QA이동버튼은상하차에서만사용가능합니다 = "QA이동 버튼은 상,하차 에서만 사용 가능합니다";

View File

@@ -154,6 +154,17 @@ namespace Project.Device
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; }
/// <summary>

View File

@@ -94,7 +94,14 @@ namespace Project
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)
{
@@ -105,7 +112,11 @@ namespace Project
voice.SpeakAsyncCancelAll();
if (voice.State == SynthesizerState.Ready)
voice.SpeakAsync(m);
if (addlog) PUB.log.Add("SPEAK", m);
if (addlog)
{
if (logcate.isEmpty()) logcate = "SPEAK";
PUB.log.Add(logcate, m);
}
}
/// <summary>
@@ -118,7 +129,7 @@ namespace Project
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] &&
VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false &&
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_UP] == false)
{
@@ -150,7 +161,7 @@ namespace Project
public static AR.Log log, logagv, logplc, logbms, logxbee;
public static Boolean bPlayMusic = false;
/// <summary>
/// 사용자 인풋 감지 시간
@@ -382,6 +393,9 @@ namespace Project
return Lang.AGV연결실패;
case eECode.PLCCONN:
return Lang.PLC통신실패;
case eECode.MESSAGE_ERROR:
if (values.Length > 0) return values[0].ToString();
return "Message Error";
default:
return err.ToString();
@@ -582,7 +596,33 @@ namespace Project
}
#region VirtualAGV
public static MapNode FindByNodeID(string nodeidx)
{
if (_mapNodes == null || _mapNodes.Any() == false) return null;
if (nodeidx.isEmpty()) return null;
return _mapNodes.Where(t => t.NodeId.Equals(nodeidx)).FirstOrDefault();
}
public static MapNode FindByRFID(string rfidValue)
{
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)
{
if (_mapNodes == null || _mapNodes.Any() == false) return null;
if (alias.isEmpty()) return null;
var lst = _mapNodes.Where(t => t.NodeAlias.Equals(alias));
if (lst.Any() == false) return null;
return lst.ToList();
}
public static List<MapNode> FindByNodeType(AGVNavigationCore.Models.MapNode type)
{
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>
/// RFID 읽기 시 해당 노드 위치로 AGV 업데이트
/// </summary>

View File

@@ -84,7 +84,7 @@ namespace Project
if (PUB.AGV.system1.stop_by_front_detect == true)
{
var tsSpeak = DateTime.Now - LastSpeakTime;
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
{
PUB.Speak(Lang.);
LastSpeakTime = DateTime.Now;
@@ -92,14 +92,86 @@ namespace Project
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)
{
case ERunStep.GOHOME:
if (_SM_RUN_GOHOME(runStepisFirst, PUB.sm.GetRunSteptime) == true)
{
PUB.log.Add($"홈 이동이 완료되어 준비상태로 전환합니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
}
break;
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}");
if (target.Type == AGVNavigationCore.Models.NodeType.Buffer)
{
//현재위치가 마지막경로의 NODEID와 일치해야한다
var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault();
if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNodeId))
{
//버퍼진입전 노드에 도착완료했따
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;
}
else if (target.Type == AGVNavigationCore.Models.NodeType.Charging)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.Loader)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.Clearner)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.UnLoader)
{
}
else
{
//목적지다 다른 형태이다
}
PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.None;
PUB.sm.SetNewRunStep(ERunStep.READY);
}
break;
case ERunStep.MARKSTROPB: //후진방향으로 마크스탑
case ERunStep.MARKSTOPB: //후진방향으로 마크스탑
case ERunStep.MARKSTOPF: //전진방향으로 마크스탑
//이동중이지 않다면 먼저 이동을 진행한다
@@ -172,423 +244,137 @@ namespace Project
PUB.Result.TargetPos = ePosition.QC;
}
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.log.AddAT("충전 해제로 대기상태로 전환 합니다");
}
else
{
//홈 이동 명령처리
PUB.sm.ClearRunStep();
PUB.Result.TargetPos = ePosition.QC;
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
PUB.Speak(Lang.);
}
}
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.GOHOME:
if (runStepisFirst)
case ERunStep.LOADER_IN: //로더도킹
if (_SM_RUN_LOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
{
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.);
PUB.Speak(Lang.);
VAR.TIME.Update(eVarTime.ChargeTry);
PUB.sm.SetNewRunStep(ERunStep.READY); //대기상태로 전환한다
//도킹완료상태를 업데이트한다.
PUB.XBE.LoaderInComplete = true;
//로더아웃으로 자동 진행 합니다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT);
return;
}
break;
case ERunStep.UNLOADER_OUT: //언로더아웃
if (_SM_RUN_UNLOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime))
{
PUB.Speak(Lang.);
//도킹완료상태를 업데이트한다.
PUB.XBE.UnloaderInComplete = false;
PUB.XBE.UnloaderOutComplete = true;
//대기상태로 전환
PUB.sm.SetNewRunStep(ERunStep.READY);
return;
}
break;
case ERunStep.UNLOADER_IN: //언로더도킹
if (_SM_RUN_UNLOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime))
{
PUB.Speak(Lang.);
//도킹완료상태를 업데이트한다.
PUB.XBE.UnloaderInComplete = true;
//언로더아웃으로 자동 진행 합니다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT);
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;
//클리너아웃으로 자동 진행 합니다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT);
return;
}
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;
//버퍼아웃으로 자동 진행 합니다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT);
return;
}
break;
}
}
bool CheckStopCondition()
{
return true;
}
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
{
//움직이지 않으므로 전진하도록 한다
PUB.log.Add($"AGV 기동 방향(DOWN):{dir}");
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
}
LastCommandTime = DateTime.Now;
}
}
else
{
var tsCmd = DateTime.Now - LastCommandTime;
if (tsCmd.TotalMilliseconds >= 1999)
{
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')
{
PUB.log.Add($"마크정지를 해제하기 위해 장비를 멈춥니다");
}
}
else
{
PUB.log.Add($"AGV 기동 방향(UP):{dir}");
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
}
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);
}
}
}
}
}
//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
}

View File

@@ -0,0 +1,237 @@
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++)
{
//리프트를 내린다.
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//리프트다운센서를 확인한다.
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
}
}
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++)
{
//완료되었다. (ACS에 보내야함)
PUB.log.Add("버퍼투입완료");
PUB.sm.UpdateRunStepSeq();
return false;
}
PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})");
return true;
}
}
}

View 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;
}
}
}

View File

@@ -14,6 +14,8 @@ namespace Project
public Boolean _SM_RUN_CHGOFF(bool isFirst, TimeSpan stepTime)
{
//충전중인지 확인한다.
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
{
@@ -25,7 +27,7 @@ namespace Project
PUB.Speak(Lang.);
}
}
//AGV는 충전을 해제한 상태이다
if (PUB.AGV.system1.Battery_charging == false)
{
@@ -47,6 +49,17 @@ namespace Project
PUB.AGV.AGVCharge(PUB.setting.ChargerID, false);
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;
}

View File

@@ -0,0 +1,155 @@
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++)
{
//리프트를 내린다.
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//리프트다운센서를 확인한다.
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
}
}
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++)
{
//완료되었다.
PUB.log.Add("클리너진입완료");
PUB.sm.UpdateRunStepSeq();
return false;
}
PUB.AddEEDB($"클리너진입완료({PUB.Result.TargetPos})");
return true;
}
}
}

View 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;
}
}
}

View File

@@ -38,7 +38,7 @@ namespace Project
if (PUB.AGV.system1.stop_by_front_detect == true)
{
var tsSpeak = DateTime.Now - LastSpeakTime;
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
{
PUB.Speak(Lang.);
LastSpeakTime = DateTime.Now;
@@ -63,8 +63,16 @@ namespace Project
var idx = 1;
if (PUB.sm.RunStepSeq == idx++)
{
PUB.Speak(Lang.);
PUB.Result.TargetPos = ePosition.QC;
var targetnode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Charger);
if(targetnode == null)
{
PUB.log.AddE($"충전기 노드가 설정되지 않았습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
//충전기로 대상을 설정한다
PUB._virtualAGV.TargetNode = targetnode;
VAR.TIME.Update(eVarTime.ChargeSearch);
PUB.sm.UpdateRunStepSeq();
PUB.log.Add($"충전:대상위치 QC 시작");
@@ -73,7 +81,7 @@ namespace Project
else if (PUB.sm.RunStepSeq == idx++)
{
//모션 전후진 제어
if (UpdateMotionPositionForMark("GOCHARGE #1") == true)
if ( UpdateMotionPositionForCharger("GOCHARGE #1") == true)
{
PUB.log.Add($"충전:충전기 검색 전 QC위치 확인 완료");
PUB.sm.UpdateRunStepSeq();
@@ -200,9 +208,6 @@ namespace Project
PUB.Result.CurrentPos = ePosition.CHARGE; //현재위치를 충전기로 한다
PUB.Result.TargetPos = ePosition.CHARGE;
PUB.Result.CurrentPosCW = "1";
ctlPos1.SetPosition(ePosition.CHARGE);
ctlPos1.SetPositionActive(ePosition.CHARGE);
ctlPos1.Invalidate();
PUB.sm.UpdateRunStepSeq();
return false;
}

View File

@@ -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;
}
}
}

View File

@@ -13,27 +13,18 @@ namespace Project
{
public Boolean _SM_RUN_GOHOME(bool isFirst, TimeSpan stepTime)
{
var funcName = "_SM_RUN_GOHOME";
if (runStepisFirst)
{
// PUB.flag.set(EFlag.FLAG_NEXTSTOP_ALIGN, false);
//VAR.BOOL[eVarBool.FLAG_NEXTSTOP_MARK] = 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 연결오류
if (PUB.AGV.IsOpen == false)
{
PUB.Result.SetResultMessage(eResult.Hardware, eECode.AGVCONN, eNextStep.ERROR);
return false;
}
//충전 상태가 OFF되어야 동작하게한다
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
@@ -43,7 +34,7 @@ namespace Project
if (PUB.AGV.system1.stop_by_front_detect == true)
{
var tsSpeak = DateTime.Now - LastSpeakTime;
if (tsSpeak.TotalSeconds >= PUB.setting.doorSoundTerm)
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
{
PUB.Speak(Lang.);
LastSpeakTime = DateTime.Now;
@@ -51,27 +42,29 @@ namespace Project
return false;
}
//현재 위치가 결정되어있는지 체크한다
if (_SM_RUN_POSCHK(isFirst, stepTime) == false)
return false;
var idx = 1;
if (PUB.sm.RunStepSeq == idx++)
{
PUB.Speak(Lang.);
//홈은 무조건 QC위치로 간다
PUB.AddEEDB($"홈검색시작({PUB.Result.TargetPos})");
PUB.Result.TargetPos = ePosition.QC;
var homenode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Home);
if (homenode == null)
{
PUB.log.Add($"홈위치의 노드맵핑(환경설정)이 없습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
PUB._virtualAGV.TargetNode = homenode;
PUB.AddEEDB($"홈검색시작({homenode.RfidId})");
PUB.sm.UpdateRunStepSeq();
return false;
}
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();
}
return false;
@@ -79,7 +72,7 @@ namespace Project
else if (PUB.sm.RunStepSeq == idx++)
{
//QC까지 모두 완료되었다.(완전히 정차할때까지 기다린다)
PUB.Speak(Lang., true);
PUB.Speak(Lang., true);
PUB.AddEEDB($"홈검색완료({PUB.Result.TargetPos})");
PUB.sm.UpdateRunStepSeq();
return false;

View File

@@ -29,6 +29,8 @@ namespace Project
VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
}
//PUB._virtualAGV.
//목적지가 설정되었는지 체크한다.
//Z if (PUB.mapctl.Manager.agv.TargetRFID.IsEmpty)
// {

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,155 @@
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_LOADER_IN(bool isFirst, TimeSpan stepTime)
{
var funcname = "_SM_RUN_LOADER_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("로더 작업을 시작합니다"); // Lang resource might not exist, using string
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//리프트를 내린다.
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//리프트다운센서를 확인한다.
var liftdown = true; // 센서 확인 로직이 주석처리되어 있거나 하드코딩되어 있었음 (버퍼 코드 참조)
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
}
}
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++)
{
//완료되었다.
PUB.log.Add("로더진입완료");
PUB.sm.UpdateRunStepSeq();
return false;
}
PUB.AddEEDB($"로더진입완료({PUB.Result.TargetPos})");
return true;
}
}
}

View File

@@ -0,0 +1,82 @@
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_LOADER_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("loader 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;
}
}
}

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using AR;
using COMM;
namespace Project
{
@@ -10,45 +12,30 @@ namespace Project
{
public Boolean _SM_RUN_POSCHK(bool isFirst, TimeSpan stepTime)
{
//현재 위치가 찾아지지 않았다면. 먼저 위치를 찾는다. 위로 이동시켜서 찾는다.
if (PUB.Result.CurrentPos == ePosition.NONE)
//현재위치가 설정되어있는지 확인한다, 현재위치값이 있는 경우 True 를 반환
var currentnode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId);
if (currentnode != null) return true;
//이동을 하지 않고있다면 전진을 진행한다
if (PUB.AGV.system1.agv_run == false)
{
if (isFirst)
var ts = VAR.TIME.RUN(eVarTime.LastRunCommandTime);
if (ts.TotalSeconds > 5)
{
PUB.Speak( Lang.);
}
if (PUB.AGV.system1.agv_run)
{
//방향을 체크한다
var basedir = PUB.setting.AGV_Direction_FVI_Backward ? 'F' : 'B';
if (PUB.AGV.data.Direction == basedir)
PUB.log.Add($"현재위치를 몰라 전진 이동 합니다");
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{
var ts = DateTime.Now - LastCommandTime;
if (ts.TotalMilliseconds >= 1999)
{
PUB.AGV.AGVMoveStop("SM_RUN_POSCHK",arDev.Narumi.eStopOpt.Stop);
LastCommandTime = DateTime.Now;
}
}
Bunki = arDev.Narumi.eBunki.Strate,
Direction = arDev.Narumi.eMoveDir.Forward,
PBSSensor = 1,
Speed = arDev.Narumi.eMoveSpd.Low,
});
PUB.AGV.AGVMoveRun();
VAR.TIME.Update(eVarTime.LastRunCommandTime);
}
else if (PUB.AGV.system1.agv_run == false)
{
//전진이동한다
var ts = DateTime.Now - LastCommandTime;
if (ts.TotalMilliseconds >= 1999)
{
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;
}
}
return false;
}
else return true;
VAR.STR[eVarString.StatusMessage] = "현재 위치를 알 수 없습니다";
return false;
}
}
}

View File

@@ -58,74 +58,6 @@ namespace Project
VAR.STR[eVarString.ChargeCheckMsg] = "수동 충전";
}
//현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다
else if (PUB.Result.CurrentPos == ePosition.NONE)
{
if (PUB.AGV.system1.agv_run == false &&
PUB.AGV.error.Emergency == false &&
PUB.AGV.error.runerror_by_no_magent_line == false)
{
//현재위치를 검색해야함
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
PUB.sm.SetNewStep(eSMStep.RUN);
PUB.AddEEDB($"READY상태에서 현재위치를 몰라 홈으로 이동");
}
VAR.STR[eVarString.ChargeCheckMsg] = "현재 위치 모름";
}
//else if (PUB.setting.Enable_AutoCharge == true )
//{
// if(bAutoChageOn)
// {
// VAR.BOOL[eVarBool.CHARGE_READY] = true;
// if (PUB.BMS.Current_Level < PUB.setting.ChargeStartLevel)
// {
// //레벨에 의한 자동 충전간격은
// var ts = VAR.TIME.RUN(eVarTime.ChargeTry);
// if (ts.TotalSeconds >= PUB.setting.ChargeRetryTerm)
// {
// VAR.I32[eVarInt32.ChargeWaitSec] = 0;
// VAR.BOOL[eVarBool.CHARGE_WAIT] = false;
// PUB.log.Add($"자동충전레벨시작 {PUB.BMS.Current_Level}/{PUB.setting.ChargeStartLevel}");
// PUB.sm.ClearRunStep();
// PUB.sm.SetNewRunStep(ERunStep.GOCHARGE);
// PUB.sm.SetNewStep(eSMStep.RUN);
// PUB.AddEEDB($"자동충전레벨시작 {PUB.BMS.Current_Level}/{PUB.setting.ChargeStartLevel}");
// }
// else
// {
// VAR.I32[eVarInt32.ChargeWaitSec] = (int)(PUB.setting.ChargeRetryTerm - ts.TotalSeconds);
// VAR.BOOL[eVarBool.CHARGE_WAIT] = true;
// }
// VAR.STR[eVarString.ChargeCheckMsg] = "배터리 충전 레벨 필요";
// }
// else
// {
// //아직 레벨이 높다
// VAR.BOOL[eVarBool.CHARGE_READY] = false;
// VAR.I32[eVarInt32.ChargeWaitSec] = 0;
// VAR.BOOL[eVarBool.CHARGE_WAIT] = false;
// VAR.STR[eVarString.ChargeCheckMsg] = "배터리 레벨상 충전 불필요";
// }
// }
// else
// {
// VAR.BOOL[eVarBool.CHARGE_READY] = false;
// VAR.I32[eVarInt32.ChargeWaitSec] = 0;
// VAR.BOOL[eVarBool.CHARGE_WAIT] = false;
// VAR.STR[eVarString.ChargeCheckMsg] = "충전 불가 조건";
// //충전조건이 맞지 않는다
// }
//}
//else
//{
// VAR.BOOL[eVarBool.CHARGE_READY] = false;
// VAR.I32[eVarInt32.ChargeWaitSec] = 0;
// VAR.BOOL[eVarBool.CHARGE_WAIT] = false;
// //자동충전이 해제된 상태이므로 처리하지 않는다
// VAR.STR[eVarString.ChargeCheckMsg] = "자동 충전 해제 됨";
//}
//대기모드에서는 움직이지 않게 한다
if(PUB.AGV.system1.agv_run)
{

View File

@@ -83,11 +83,6 @@ namespace Project
PUB.Result.TargetPos = ePosition.NONE;
PUB.Result.CurrentPos = ePosition.NONE;
PUB.Result.CurrentPosCW = "0";
ctlPos1.SetPositionDeActive();
ctlPos1.SetPosition(ePosition.NONE);
ctlPos1.SetDirection("0");
ctlPos1.Invalidate();
PUB.sm.UpdateRunStepSeq();
return false;
}

View File

@@ -79,6 +79,10 @@ namespace Project
synlist.Add("STT", PUB.setting.STT.ToString("0000"));
//synlist.Add("SBN", PUB.setting.ChargerID.ToString("0000"));
synlist.Add("SGS", PUB.setting.GDSValue.ToString("0000"));
VAR.I32[eVarInt32.SyncItemCount] = synlist.Count;
PUB.AddEEDB($"SYNC시작({PUB.Result.TargetPos})");
synidx = 0;
@@ -97,12 +101,19 @@ namespace Project
}
else if (PUB.sm.RunStepSeq == idx++)
{
//통신 확인되었으므로 스피드를 전송한다.
//통신 확인되었으므로 설정값들을
if (synidx < synlist.Count)
{
var item = synlist.ElementAt(synidx);
UpdateProgressStatus(stepTime.TotalSeconds, 5, $"SYNC :{item.Key}");
PUB.AGV.AGVCommand(item.Key, item.Value);
// 캔버스에 동기화 상태 표시
if (PUB._mapCanvas != null)
{
float progress = (float)synidx / VAR.I32[eVarInt32.SyncItemCount];
PUB._mapCanvas.SetSyncStatus("장비 설정 동기화 중...", progress, $"항목: {item.Key} ({synidx + 1}/{synlist.Count})");
}
}
LastCommandTime = DateTime.Now;
PUB.sm.UpdateRunStepSeq();
@@ -139,6 +150,11 @@ namespace Project
{
PUB.AddEEDB($"SYNC완료({PUB.Result.TargetPos})");
UpdateProgressStatus(stepTime.TotalSeconds, 5, "SYNC : 완료");
// 동기화 완료 시 캔버스 모드 복귀
if (PUB._mapCanvas != null)
PUB._mapCanvas.SetSyncStatus("동기화 완료!", 1.0f, "잠시 후 메인 화면으로 이동합니다.");
LastCommandTime = DateTime.Now;
PUB.sm.UpdateRunStepSeq();
return false;

View File

@@ -0,0 +1,155 @@
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_UNLOADER_IN(bool isFirst, TimeSpan stepTime)
{
var funcname = "_SM_RUN_UNLOADER_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++)
{
//리프트를 내린다.
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//리프트다운센서를 확인한다.
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10)
{
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
}
}
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++)
{
//완료되었다.
PUB.log.Add("언로더진입완료");
PUB.sm.UpdateRunStepSeq();
return false;
}
PUB.AddEEDB($"언로더진입완료({PUB.Result.TargetPos})");
return true;
}
}
}

View File

@@ -0,0 +1,81 @@
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_UNLOADER_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("unloader 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;
}
}
}

View File

@@ -0,0 +1,248 @@
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
{
bool CheckStopCondition()
{
return true;
}
/// <summary>
/// 라이더멈춤이 설정되어있다면 음성으로 알려준다
/// </summary>
/// <returns></returns>
public bool CheckLiderStop()
{
if (PUB.AGV.system1.stop_by_front_detect == true)
{
var tsSpeak = DateTime.Now - LastSpeakTime;
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
{
PUB.Speak(Lang.);
LastSpeakTime = DateTime.Now;
}
return false;
}
return true;
}
/// <summary>
/// 설정된 목적지까지 이동을 완료 한 후 True를 반환합니다.
/// 목적지 : PUB._virtualAGV.TargetNode
///
/// </summary>
/// <param name="sender"></param>
/// <returns></returns>
Boolean UpdateMotionPositionForMark(string sender)
{
//현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다
if (_SM_RUN_POSCHK(false, new TimeSpan()) == false) return false;
//현재위치노드 오류
var currentNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId);
if (currentNode == null)
{
PUB.log.AddE($"현재위치노드가 없습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
//시작노드값이 없다면 현재위치를 노드로 결정한다
if (PUB._virtualAGV.StartNode == null)
PUB._virtualAGV.StartNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId);
//시작노드가없다면 오류
if (PUB._virtualAGV.StartNode == null)
{
PUB.log.AddE($"경로시작노드가 없습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
//대상노드가 없다면 오류
if (PUB._virtualAGV.TargetNode == null)
{
PUB.log.AddE($"경로종료노드가 없습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
//경로 생성(경로정보가 없거나 현재노드가 경로에 없는경우)
if (PUB._virtualAGV.CurrentPath == null ||
PUB._virtualAGV.CurrentPath.DetailedPath.Any() == false ||
PUB._virtualAGV.CurrentPath.DetailedPath.Where(t => t.NodeId.Equals(currentNode.NodeId)).Any() == false)
{
if (PUB.AGV.system1.agv_run)
{
PUB.log.Add($"경로 재생성으로 인해 구동을 멈춥니다");
PUB.AGV.AGVMoveStop("경로재생성");
}
var PathResult = CalcPath(PUB._virtualAGV.StartNode, PUB._virtualAGV.TargetNode);
if (PathResult.result == null)
{
PUB.log.AddE($"경로가 계산되지 않았습니다");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
PUB.log.AddI($"경로생성 {PUB._virtualAGV.StartNode.RfidId} -> {PUB._virtualAGV.TargetNode.RfidId}");
}
//predict 를 이용하여 다음 이동을 모두 확인한다.
var nextAction = PUB._virtualAGV.Predict();
var message = $"[다음 행동 예측]\n\n" +
$"모터: {nextAction.Motor}\n" +
$"마그넷: {nextAction.Magnet}\n" +
$"속도: {nextAction.Speed}\n" +
$"이유: {nextAction.Message}\n\n" +
$"---\n" +
$"현재 상태: {PUB._virtualAGV.CurrentState}\n" +
$"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" +
$"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" +
$"현재 노드: {PUB._virtualAGV.CurrentNodeId ?? ""}";
//모터에서 정지를 요청했다
if (nextAction.Motor == AGVNavigationCore.Models.MotorCommand.Stop)
{
if (PUB.AGV.system1.agv_run)
{
// 완료(Complete) 상태라면 MarkStop 전송
if (nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.MarkStop)
{
PUB.log.Add("다음행동예측에서 MARK STOP이 확인되었습니다");
PUB.AGV.AGVMoveStop(nextAction.Message, arDev.Narumi.eStopOpt.MarkStop);
}
else
{
PUB.log.Add("다음행동예측에서 장비 멈춤이 확인되었습니다");
PUB.AGV.AGVMoveStop(nextAction.Message);
}
}
// 목적지 도착 여부 확인
// 현재 노드가 타겟 노드와 같고, 위치가 확정된 상태라면 도착으로 간주
// 단, AGV가 실제로 멈췄는지 확인 (agv_run == false)
if (PUB._virtualAGV.IsPositionConfirmed &&
PUB._virtualAGV.CurrentNodeId == PUB._virtualAGV.TargetNode.NodeId)
{
if (PUB.AGV.system1.agv_run == false)
{
return true;
}
}
return false;
}
else
{
// 이동 명령 변환 (AGVNavigationCore -> arDev.Narumi)
var bunki = arDev.Narumi.eBunki.Strate;
if (nextAction.Magnet == AGVNavigationCore.Models.MagnetPosition.L) bunki = arDev.Narumi.eBunki.Left;
else if (nextAction.Magnet == AGVNavigationCore.Models.MagnetPosition.R) bunki = arDev.Narumi.eBunki.Right;
var dir = arDev.Narumi.eMoveDir.Forward;
if (nextAction.Motor == AGVNavigationCore.Models.MotorCommand.Backward) dir = arDev.Narumi.eMoveDir.Backward;
var spd = arDev.Narumi.eMoveSpd.Low;
if (nextAction.Speed == AGVNavigationCore.Models.SpeedLevel.M) spd = arDev.Narumi.eMoveSpd.Mid;
else if (nextAction.Speed == AGVNavigationCore.Models.SpeedLevel.H) spd = arDev.Narumi.eMoveSpd.High;
// 명령 설정
// 현재 상태와 다를 때만 전송 (불필요한 통신 부하 방지)
if (PUB.AGV.data.Sts != bunki.ToString()[0] ||
PUB.AGV.data.Direction != dir.ToString()[0] ||
PUB.AGV.data.Speed != spd.ToString()[0])
{
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{
Bunki = bunki,
Direction = dir,
PBSSensor = 1,
Speed = spd,
});
PUB.log.Add($"Predict Run Setting = bunki:{bunki},dir:{dir},pbs:1,spd:{spd}");
}
// AGV가 정지 상태라면 구동 시작
if (PUB.AGV.system1.agv_run == false)
{
var runOpt = (dir == arDev.Narumi.eMoveDir.Forward) ? arDev.Narumi.eRunOpt.Forward : arDev.Narumi.eRunOpt.Backward;
PUB.AGV.AGVMoveRun(runOpt);
}
return false;
}
}
/// <summary>
/// 충전기검색시퀀스
/// </summary>
/// <param name="sender"></param>
/// <returns></returns>
Boolean UpdateMotionPositionForCharger(string sender)
{
if (VAR.BOOL[eVarBool.AGVDIR_BACK] == 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.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{
Bunki = arDev.Narumi.eBunki.Strate,
Direction = arDev.Narumi.eMoveDir.Forward,
PBSSensor = 1,
Speed = arDev.Narumi.eMoveSpd.Low,
});
PUB.AGV.AGVMoveRun();//
tm_gocharge_command = DateTime.Now;
}
}
else
{
//현재위치가 충전위치이고, 움직이지 않았다면 완료된 경우라 할수 있따
if (PUB._virtualAGV.CurrentNodeId.Equals(PUB.setting.NodeMAP_RFID_Charger) &&
VAR.BOOL[eVarBool.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
}

View File

@@ -49,82 +49,48 @@ namespace Project
case arDev.Narumi.DataType.STS:
{
//마크센서 확인
var chg_mark1 = PUB.AGV.signal.GetChanged(arDev.Narumi.Signal.eflag.mark_sensor_1);
var chg_mark2 = PUB.AGV.signal.GetChanged(arDev.Narumi.Signal.eflag.mark_sensor_1);
var chg_run = PUB.AGV.system1.GetChanged(arDev.Narumi.SystemFlag1.eflag.agv_run);
var chg_stop = PUB.AGV.system1.GetChanged(arDev.Narumi.SystemFlag1.eflag.agv_stop);
var agv_err = PUB.AGV.error.Value;
var agv_emg = PUB.AGV.error.Emergency;
var agv_chg = PUB.AGV.system1.Battery_charging;
var agv_stp = PUB.AGV.system1.agv_stop;
var agv_run = PUB.AGV.system1.agv_run;
var agv_mrk = PUB.AGV.signal.mark_sensor;
//if (chg_run && PUB.AGV.system1.agv_run) PUB.Speak("이동을 시작 합니다");
VAR.BOOL[eVarBool.AGVDIR_UP] = PUB.AGV.data.Direction == 'B';
VAR.BOOL[eVarBool.AGV_ERROR] = PUB.AGV.error.Value > 0;
VAR.BOOL[eVarBool.EMERGENCY] = PUB.AGV.error.Emergency;
VAR.BOOL[eVarBool.AGVDIR_BACK] = PUB.AGV.data.Direction == 'B';
////모터방향 입력
//if (PUB.AGV.data.Direction == 'B')
// PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Backward;
//else
// PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Forward;
////현재 속도
//if (PUB.AGV.data.Speed == 'H')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.High;
//else if (PUB.AGV.data.Speed == 'M')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Middle;
//else if (PUB.AGV.data.Speed == 'L')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Low;
//else if (PUB.AGV.data.Speed == 'S')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.MarkStop;
////이동방향
//if (PUB.AGV.data.Sts == 'S')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Straight;
//else if (PUB.AGV.data.Sts == 'L')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Left;
//else if (PUB.AGV.data.Sts == 'R')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Right;
//PUB.mapctl.Manager.agv.IsMoving = PUB.AGV.system1.agv_run;
//PUB.mapctl.Manager.agv.IsMarkCheck = PUB.AGV.system1.Mark1_check || PUB.AGV.system1.Mark2_check;
if (PUB.AGV.signal.mark_sensor == false)
if (VAR.BOOL[eVarBool.AGV_ERROR] != (agv_err > 0))
{
if (VAR.BOOL[eVarBool.MARK_SENSOROFF] == false)
{
VAR.BOOL[eVarBool.MARK_SENSOROFF] = true;
VAR.TIME[eVarTime.MarkSensorOff] = DateTime.Now;
PUB.log.Add($"마크센서off를 설정");
}
VAR.BOOL[eVarBool.AGV_ERROR] = (agv_err > 0);
PUB.logagv.Add($"new AGV_ERROR ({PUB.AGV.error.Value})");
}
else
if (VAR.BOOL[eVarBool.EMERGENCY] != agv_emg)
{
if (VAR.BOOL[eVarBool.MARK_SENSOROFF] == true)
{
VAR.BOOL[eVarBool.MARK_SENSOROFF] = false;
VAR.TIME[eVarTime.MarkSensorOff] = DateTime.Now;
PUB.log.Add($"마크센서off를 해제");
}
VAR.BOOL[eVarBool.EMERGENCY] = agv_emg;
PUB.logagv.Add($"new EMERGENCY ({VAR.BOOL[eVarBool.EMERGENCY]})");
}
//차징상태변경
if (_charging != PUB.AGV.system1.Battery_charging)
if (_charging != agv_chg)
{
if (PUB.AGV.system1.Battery_charging)
if (agv_chg)
{
VAR.TIME[eVarTime.ChargeStart] = DateTime.Now;
PUB.logagv.Add($"충전시작:{VAR.TIME[eVarTime.ChargeStart]}");
}
_charging = PUB.AGV.system1.Battery_charging;
_charging = agv_chg;
}
//배터리충전상태
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] != PUB.AGV.system1.Battery_charging)
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] != agv_chg)
{
PUB.log.Add($"충전상태전환 {PUB.AGV.system1.Battery_charging}");
VAR.BOOL[eVarBool.FLAG_CHARGEONA] = PUB.AGV.system1.Battery_charging;
PUB.log.Add($"충전상태전환 {agv_chg}");
VAR.BOOL[eVarBool.FLAG_CHARGEONA] = agv_chg;
}
//자동충전해제시 곧바로 수동 충전되는 경우가 있어 자동 상태를 BMS에 넣는다 230118
PUB.BMS.AutoCharge = PUB.AGV.system1.Battery_charging;
PUB.BMS.AutoCharge = agv_chg;
if (PUB.AGV.error.Charger_pos_error != VAR.BOOL[eVarBool.CHG_POSERR])
{
@@ -135,41 +101,41 @@ namespace Project
VAR.BOOL[eVarBool.CHG_POSERR] = PUB.AGV.error.Charger_pos_error;
}
if (VAR.BOOL[eVarBool.MARK_SENSOROFF] == true && PUB.AGV.signal.mark_sensor == false)
//나르미가 멈췄다면 다음 마크 이동 기능이 OFF 된다
if (agv_stp)
{
//현재 활성화된 위치를 꺼준다
if (this.ctlPos1.GetPositionActive(PUB.Result.CurrentPos))
if (VAR.BOOL[eVarBool.NEXTSTOP_MARK])
{
var ts = VAR.TIME.RUN(eVarTime.MarkSensorOff);
if (ts.TotalSeconds >= 2)
{
ctlPos1.SetPositionDeActive();
PUB.log.Add($"현재 활성위치를 해제 함");
}
VAR.BOOL[eVarBool.NEXTSTOP_MARK] = false;
PUB.logagv.Add($"NEXTSTOP_MARK 변경({VAR.BOOL[eVarBool.NEXTSTOP_MARK]})");
}
}
//나르미가 멈췄다면 다음 마크 이동 기능이 OFF 된다
if (PUB.AGV.system1.agv_stop)
VAR.BOOL[eVarBool.NEXTSTOP_MARK] = false;
//마크센서 상태가 변경이 되었다면
if (VAR.BOOL[eVarBool.MARK_SENSOR] != PUB.AGV.signal.mark_sensor)
{
if (PUB.AGV.signal.mark_sensor)
{
//현재위치를 확정한다
var curact = ctlPos1.GetPositionActive(PUB.Result.CurrentPos);
if (curact == false)
{
PUB.log.Add($"마크센서로인해 현재위치 설정완료:{PUB.Result.CurrentPos}");
ctlPos1.SetPositionActive(PUB.Result.CurrentPos);
ctlPos1.SetDirection("");
ctlPos1.Invalidate();
}
}
PUB.logagv.Add($"MARK_SENSOR 변경({PUB.AGV.signal.mark_sensor})");
VAR.BOOL[eVarBool.MARK_SENSOR] = PUB.AGV.signal.mark_sensor;
//AGV가 멈췄고 마크센서가 ON되었다면 마지막 RFID 위치가 확정된경우이다
if (agv_stp && VAR.BOOL[eVarBool.MARK_SENSOR])
{
PUB.log.Add("마크스탑이 확인되어 최종위치를 PASS 처리 합니다");
var curnode = PUB._virtualAGV.SetCurrentNodeMarkStop();
if (curnode == true)
{
PUB.log.Add($"마크스탑으로 해당노드의 이동을 확정합니다");
}
else PUB.log.AddAT($"마크스탑이 확인되었으나 현재 노드가없어 PASS를 설정하지 못함");
}
}
if (VAR.BOOL[eVarBool.MARK_SENSOROFF] != VAR.BOOL[eVarBool.MARK_SENSOR])
{
VAR.BOOL[eVarBool.MARK_SENSOROFF] = !VAR.BOOL[eVarBool.MARK_SENSOR];
VAR.TIME[eVarTime.MarkSensorOff] = DateTime.Now;
PUB.log.Add($"MARK_SENSOROFF 변경({VAR.BOOL[eVarBool.MARK_SENSOROFF]})");
}
}
break;
case arDev.Narumi.DataType.TAG:
@@ -239,8 +205,23 @@ namespace Project
}
//이 후 상황을 예측한다
var command = PUB._virtualAGV.Predict();
var preditMSG = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
if (PUB._mapCanvas != null)
{
var nextAction = PUB._virtualAGV.Predict();
var message = $"[다음 행동 예측]\n\n" +
$"모터: {nextAction.Motor}\n" +
$"마그넷: {nextAction.Magnet}\n" +
$"속도: {nextAction.Speed}\n" +
$"이유: {nextAction.Message}\n\n" +
$"---\n" +
$"현재 상태: {PUB._virtualAGV.CurrentState}\n" +
$"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" +
$"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" +
$"현재 노드: {PUB._virtualAGV.CurrentNodeId ?? ""}";
PUB._mapCanvas.PredictMessage = message;
}
}
catch (Exception ex)
{

View File

@@ -156,6 +156,15 @@ namespace Project
// 장치 관리 태스크 시작 (IDLE 진입 시 한 번만)
StartDeviceManagementTask();
// 동기화 모드 종료 (혹시 남아있을 경우)
if (PUB._mapCanvas != null)
{
this.Invoke(new Action(() => {
PUB._mapCanvas.ExitSyncMode();
}));
}
}
//자동소거기능
@@ -179,6 +188,12 @@ namespace Project
break;
case eSMStep.SYNC:
if(e.isFirst)
{
// 동기화 완료 시 캔버스 모드 복귀
if (PUB._mapCanvas != null)
PUB._mapCanvas.SetSyncStatus("설정 동기화", 0f, "환경설정 값으로 AGV컨트롤러를 설정 합니다");
}
if (_SM_RUN_SYNC(runStepisFirst, PUB.sm.GetRunSteptime))
{
var b1 = PUB.XBE.IsOpen;

View File

@@ -241,6 +241,7 @@ namespace Project
var bCharge =
(PUB.sm.RunStep == ERunStep.GOCHARGE || PUB.sm.RunStep == ERunStep.CHARGECHECK || VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true);
//가동, 목적지가 충전기라면 홈 이동이다
var bHome = PUB.sm.Step == eSMStep.RUN && (PUB.sm.RunStep == ERunStep.GOHOME);
//var bChargeM = PUB.flag.get(EFlag.FLAG_CHARGEONM);
var bAuto = VAR.BOOL[eVarBool.FLAG_AUTORUN];
@@ -368,7 +369,7 @@ namespace Project
}
IOState.setTitle(inputrow, inputcol, "RUN"); IOState.setValue(inputrow, inputcol++, (PUB.AGV.system1.agv_run ? 1 : 0));
IOState.setTitle(inputrow, inputcol, "MARK"); IOState.setValue(inputrow, inputcol++, (PUB.AGV.signal.mark_sensor ? 1 : (VAR.BOOL[eVarBool.NEXTSTOP_MARK] ? 2 : 0)));
IOState.setTitle(inputrow, inputcol, "MARK"); IOState.setValue(inputrow, inputcol++, (VAR.BOOL[eVarBool.MARK_SENSOR] ? 1 : (VAR.BOOL[eVarBool.NEXTSTOP_MARK] ? 2 : 0)));
IOState.setTitle(inputrow, inputcol, "CHG"); IOState.setValue(inputrow, inputcol++, (PUB.AGV.system1.Battery_charging ? 1 : 0));
IOState.setTitle(inputrow, inputcol, "ITM"); IOState.setValue(inputrow, inputcol++, (VAR.BOOL[eVarBool.ITEMON] ? 1 : 0));
IOState.Invalidate();
@@ -697,7 +698,7 @@ namespace Project
}
else
{
if (PUB.sm.RunStep == ERunStep.GODOWN)
if (PUB.sm.RunStep == ERunStep.BUFFER_IN)
{
if (PUB.sm.RunStepSeq == 1)
{
@@ -705,7 +706,7 @@ namespace Project
}
else UpdateStatusMessage(String.Format("하차 이동 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.GOUP)
else if (PUB.sm.RunStep == ERunStep.BUFFER_OUT)
{
if (PUB.sm.RunStepSeq == 1)
{
@@ -713,6 +714,30 @@ namespace Project
}
else UpdateStatusMessage(String.Format("상차 이동 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.LOADER_IN)
{
UpdateStatusMessage(String.Format("로더 진입 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.LOADER_OUT)
{
UpdateStatusMessage(String.Format("로더 배출 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.UNLOADER_IN)
{
UpdateStatusMessage(String.Format("언로더 진입 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.UNLOADER_OUT)
{
UpdateStatusMessage(String.Format("언로더 배출 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.CLEANER_IN)
{
UpdateStatusMessage(String.Format("클리너 진입 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.CLEANER_OUT)
{
UpdateStatusMessage(String.Format("클리너 배출 중 ({0})", PUB.Result.TargetPos), Color.Lime, Color.Black);
}
else if (PUB.sm.RunStep == ERunStep.GOCHARGE)
{
if (PUB.Result.result_message.isEmpty() == true)

View File

@@ -53,7 +53,8 @@ namespace Project
PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}");
return;
}else
}
else
{
PUB.log.AddI($"XBEE:현재위치설정:[{node.RfidId}]{node.NodeId}");
}
@@ -69,6 +70,17 @@ namespace Project
{
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
var targetNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == currTag);
//자동상태가아니라면 처리하지 않는다.
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false)
{
PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}");
}
//목적지
PUB._virtualAGV.TargetNode = targetNode;
if (targetNode == null)
{
PUB.log.AddE($"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
@@ -76,24 +88,37 @@ namespace Project
return;
}
///출발지
var startNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNodeId);
PUB._virtualAGV.StartNode = startNode;
if (startNode == null)
{
PUB.log.AddE($"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{PUB._virtualAGV.CurrentNodeId}");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{PUB._virtualAGV.CurrentNodeId}");
return;
PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNodeId}");
}
var rltGoto = CalcPath(startNode, targetNode);
if (rltGoto.result == false)
if (startNode != null)
{
PUB.log.AddE($"[{logPrefix}-Goto] {rltGoto.message}");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.Goto, rltGoto.message);
return;
//시작위치가 존재한다면 경로를 예측한다.
var rltGoto = CalcPath(startNode, targetNode);
if (rltGoto.result == null)
{
PUB.log.AddE($"[{logPrefix}-Goto] 경로예측실패 {rltGoto.message}");
}
else
{
//경로예측을 화면에 표시해준다.
PUB._virtualAGV.SetPath(rltGoto.result);
var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapNodes, nodeId)).ToList();
PUB.log.Add($"경로예측결과:{pathWithRfid}");
}
}
//대상이동으로 처리한다.
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO);
//Move to
PUB.log.Add($"[{logPrefix}-Goto] {startNode.RfidId} -> {targetNode.RfidId}");
}
else PUB.log.AddE($"[{logPrefix}-Goto] TagString Lenght Errorr:{data.Length}");
break;
@@ -135,7 +160,7 @@ namespace Project
var bunkidata = new arDev.Narumi.BunkiData();
//speed;
if (AutSpeed == 1) bunkidata.Speed = arDev.Narumi.eMoveSpd.Middle;
if (AutSpeed == 1) bunkidata.Speed = arDev.Narumi.eMoveSpd.Mid;
else if (AutSpeed == 2) bunkidata.Speed = arDev.Narumi.eMoveSpd.High;
else bunkidata.Speed = arDev.Narumi.eMoveSpd.Low;
@@ -164,7 +189,7 @@ namespace Project
arDev.Narumi.LiftCommand LCmd = arDev.Narumi.LiftCommand.STP;
if (LiftCommand == 1) LCmd = arDev.Narumi.LiftCommand.UP;
else if (LiftCommand == 2) LCmd = arDev.Narumi.LiftCommand.DN;
PUB.log.Add($"[{logPrefix}-LiftControl] {LCmd}");
PUB.AGV.LiftControl(LCmd); //리프트제어
break;
@@ -173,13 +198,13 @@ namespace Project
var chargeAction = data[1] == 1; //0= off, 1=on
//충전시퀀스가 진행되지 않았다면 진행한다
if(PUB.sm.RunStep == StateMachine.ERunStep.GOCHARGE && PUB.sm.RunStepNew != StateMachine.ERunStep.GOCHARGE)
if (PUB.sm.RunStep == StateMachine.ERunStep.GOCHARGE && PUB.sm.RunStepNew != StateMachine.ERunStep.GOCHARGE)
{
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOCHARGE);
PUB.log.AddI($"충전을 시작합니다");
}
break;
}
}
}
@@ -191,12 +216,12 @@ namespace Project
}
AGVNavigationCore.PathFinding.Planning.AGVPathfinder _advancedPathfinder = null;
(bool result, string message) CalcPath(MapNode startNode, MapNode targetNode)
(AGVNavigationCore.PathFinding.Core.AGVPathResult result, string message) CalcPath(MapNode startNode, MapNode targetNode)
{
var _mapNodes = PUB._mapNodes;
// 시작 RFID가 없으면 AGV 현재 위치로 설정
if (startNode == null || targetNode == null)
return (false, "시작 RFID와 목표 RFID를 선택해주세요.");
return (null, "시작 RFID와 목표 RFID를 선택해주세요.");
//경로계산기확인
if (_advancedPathfinder == null)
@@ -216,6 +241,7 @@ namespace Project
var _simulatorCanvas = PUB._mapCanvas;
_simulatorCanvas.FitToNodes();
string Message = string.Empty;
if (advancedResult.Success)
{
// 도킹 검증이 없는 경우 추가 검증 수행
@@ -234,14 +260,17 @@ namespace Project
// 고급 경로 디버깅 정보 표시
//UpdateAdvancedPathDebugInfo(advancedResult);
return (true, string.Empty);
}
else
{
// 경로 실패시 디버깅 정보 초기화
//_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";
return (false, $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}");
advancedResult = null;
Message = $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}";
}
return (advancedResult, Message);
}

View File

@@ -138,7 +138,10 @@ namespace Project.ViewForm
private void button9_Click(object sender, EventArgs e)
{
PUB.AGV.TurnGDSCenterScope();
var inputbox = AR.UTIL.InputBox("input value", "1000");
if (inputbox.Item1 == false) return;
if (UInt16.TryParse(inputbox.Item2, out UInt16 value) == false) return;
PUB.AGV.TurnGDSCenterScope(value);
if (PUB.sm.Step == StateMachine.eSMStep.RUN)
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
}

View File

@@ -41,46 +41,45 @@
this.btErrReset = new System.Windows.Forms.Button();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.panel2 = new System.Windows.Forms.Panel();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.btBack180 = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
this.btRight180 = new System.Windows.Forms.Button();
this.panel10 = new System.Windows.Forms.Panel();
this.btLeft180 = new System.Windows.Forms.Button();
this.button1 = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.radpbs2 = new AGVControl.MyRadioButton();
this.panel9 = new System.Windows.Forms.Panel();
this.radpbs1 = new AGVControl.MyRadioButton();
this.panel3 = new System.Windows.Forms.Panel();
this.radpbs0 = new AGVControl.MyRadioButton();
this.grpSpeed = new System.Windows.Forms.GroupBox();
this.radspdh = new AGVControl.MyRadioButton();
this.panel8 = new System.Windows.Forms.Panel();
this.radspdm = new AGVControl.MyRadioButton();
this.panel4 = new System.Windows.Forms.Panel();
this.radspdl = new AGVControl.MyRadioButton();
this.grpBunki = new System.Windows.Forms.GroupBox();
this.radright = new AGVControl.MyRadioButton();
this.panel7 = new System.Windows.Forms.Panel();
this.radstrai = new AGVControl.MyRadioButton();
this.panel5 = new System.Windows.Forms.Panel();
this.radleft = new AGVControl.MyRadioButton();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.arLabel1 = new arCtl.arLabel();
this.panel12 = new System.Windows.Forms.Panel();
this.radbackward = new AGVControl.MyRadioButton();
this.panel6 = new System.Windows.Forms.Panel();
this.radforward = new AGVControl.MyRadioButton();
this.label1 = new System.Windows.Forms.Label();
this.radpbs2 = new AGVControl.MyRadioButton();
this.radpbs1 = new AGVControl.MyRadioButton();
this.radpbs0 = new AGVControl.MyRadioButton();
this.radspdh = new AGVControl.MyRadioButton();
this.radspdm = new AGVControl.MyRadioButton();
this.radspdl = new AGVControl.MyRadioButton();
this.radright = new AGVControl.MyRadioButton();
this.radstrai = new AGVControl.MyRadioButton();
this.radleft = new AGVControl.MyRadioButton();
this.radbackward = new AGVControl.MyRadioButton();
this.radforward = new AGVControl.MyRadioButton();
this.guideSensor1 = new Narumi.UC.GuideSensor();
this.btBack180 = new System.Windows.Forms.Button();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.button2 = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout();
this.panel2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.groupBox1.SuspendLayout();
this.grpSpeed.SuspendLayout();
this.grpBunki.SuspendLayout();
this.groupBox2.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
@@ -105,7 +104,7 @@
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(604, 555);
this.tableLayoutPanel1.Size = new System.Drawing.Size(604, 594);
this.tableLayoutPanel1.TabIndex = 7;
//
// btStart
@@ -125,7 +124,7 @@
this.btStart.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btStart.GradientRepeatBG = false;
this.btStart.isButton = true;
this.btStart.Location = new System.Drawing.Point(204, 188);
this.btStart.Location = new System.Drawing.Point(204, 201);
this.btStart.MouseDownColor = System.Drawing.Color.Yellow;
this.btStart.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btStart.msg = new string[] {
@@ -147,7 +146,7 @@
this.btStart.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btStart.SignColor = System.Drawing.Color.Yellow;
this.btStart.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btStart.Size = new System.Drawing.Size(195, 179);
this.btStart.Size = new System.Drawing.Size(195, 192);
this.btStart.TabIndex = 6;
this.btStart.Text = "RUN";
this.btStart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -172,7 +171,7 @@
this.btRight.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btRight.GradientRepeatBG = false;
this.btRight.isButton = true;
this.btRight.Location = new System.Drawing.Point(405, 188);
this.btRight.Location = new System.Drawing.Point(405, 201);
this.btRight.MouseDownColor = System.Drawing.Color.Yellow;
this.btRight.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btRight.msg = null;
@@ -192,7 +191,7 @@
this.btRight.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btRight.SignColor = System.Drawing.Color.Yellow;
this.btRight.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btRight.Size = new System.Drawing.Size(196, 179);
this.btRight.Size = new System.Drawing.Size(196, 192);
this.btRight.TabIndex = 0;
this.btRight.Text = "좌회전";
this.btRight.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -238,7 +237,7 @@
this.btBack.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btBack.SignColor = System.Drawing.Color.Yellow;
this.btBack.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btBack.Size = new System.Drawing.Size(195, 179);
this.btBack.Size = new System.Drawing.Size(195, 192);
this.btBack.TabIndex = 0;
this.btBack.Text = "후진";
this.btBack.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -263,7 +262,7 @@
this.btForward.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btForward.GradientRepeatBG = false;
this.btForward.isButton = true;
this.btForward.Location = new System.Drawing.Point(204, 373);
this.btForward.Location = new System.Drawing.Point(204, 399);
this.btForward.MouseDownColor = System.Drawing.Color.Yellow;
this.btForward.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btForward.msg = null;
@@ -283,7 +282,7 @@
this.btForward.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btForward.SignColor = System.Drawing.Color.Yellow;
this.btForward.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btForward.Size = new System.Drawing.Size(195, 179);
this.btForward.Size = new System.Drawing.Size(195, 192);
this.btForward.TabIndex = 0;
this.btForward.Text = "전진";
this.btForward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -308,7 +307,7 @@
this.btLeft.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btLeft.GradientRepeatBG = false;
this.btLeft.isButton = true;
this.btLeft.Location = new System.Drawing.Point(3, 188);
this.btLeft.Location = new System.Drawing.Point(3, 201);
this.btLeft.MouseDownColor = System.Drawing.Color.Yellow;
this.btLeft.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btLeft.msg = null;
@@ -328,7 +327,7 @@
this.btLeft.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btLeft.SignColor = System.Drawing.Color.Yellow;
this.btLeft.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btLeft.Size = new System.Drawing.Size(195, 179);
this.btLeft.Size = new System.Drawing.Size(195, 192);
this.btLeft.TabIndex = 0;
this.btLeft.Text = "우회전";
this.btLeft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -342,7 +341,7 @@
this.btMarkStop.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btMarkStop.Location = new System.Drawing.Point(3, 3);
this.btMarkStop.Name = "btMarkStop";
this.btMarkStop.Size = new System.Drawing.Size(195, 179);
this.btMarkStop.Size = new System.Drawing.Size(195, 192);
this.btMarkStop.TabIndex = 7;
this.btMarkStop.Text = "마크정지";
this.btMarkStop.UseVisualStyleBackColor = true;
@@ -352,9 +351,9 @@
//
this.btchargeOff.Dock = System.Windows.Forms.DockStyle.Fill;
this.btchargeOff.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btchargeOff.Location = new System.Drawing.Point(405, 373);
this.btchargeOff.Location = new System.Drawing.Point(405, 399);
this.btchargeOff.Name = "btchargeOff";
this.btchargeOff.Size = new System.Drawing.Size(196, 179);
this.btchargeOff.Size = new System.Drawing.Size(196, 192);
this.btchargeOff.TabIndex = 8;
this.btchargeOff.Text = "충전해제";
this.btchargeOff.UseVisualStyleBackColor = true;
@@ -364,9 +363,9 @@
//
this.btChargeOn.Dock = System.Windows.Forms.DockStyle.Fill;
this.btChargeOn.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btChargeOn.Location = new System.Drawing.Point(3, 373);
this.btChargeOn.Location = new System.Drawing.Point(3, 399);
this.btChargeOn.Name = "btChargeOn";
this.btChargeOn.Size = new System.Drawing.Size(195, 179);
this.btChargeOn.Size = new System.Drawing.Size(195, 192);
this.btChargeOn.TabIndex = 9;
this.btChargeOn.Text = "충전";
this.btChargeOn.UseVisualStyleBackColor = true;
@@ -378,7 +377,7 @@
this.btErrReset.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btErrReset.Location = new System.Drawing.Point(405, 3);
this.btErrReset.Name = "btErrReset";
this.btErrReset.Size = new System.Drawing.Size(196, 179);
this.btErrReset.Size = new System.Drawing.Size(196, 192);
this.btErrReset.TabIndex = 10;
this.btErrReset.Text = "오류소거";
this.btErrReset.UseVisualStyleBackColor = true;
@@ -391,7 +390,7 @@
//
// panel2
//
this.panel2.Controls.Add(this.groupBox3);
this.panel2.Controls.Add(this.tableLayoutPanel2);
this.panel2.Controls.Add(this.button1);
this.panel2.Controls.Add(this.groupBox1);
this.panel2.Controls.Add(this.grpSpeed);
@@ -400,79 +399,33 @@
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel2.Location = new System.Drawing.Point(607, 21);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(404, 517);
this.panel2.Size = new System.Drawing.Size(404, 556);
this.panel2.TabIndex = 9;
this.panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.panel2_Paint);
//
// groupBox3
//
this.groupBox3.Controls.Add(this.btBack180);
this.groupBox3.Controls.Add(this.panel1);
this.groupBox3.Controls.Add(this.btRight180);
this.groupBox3.Controls.Add(this.panel10);
this.groupBox3.Controls.Add(this.btLeft180);
this.groupBox3.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox3.Location = new System.Drawing.Point(0, 450);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Padding = new System.Windows.Forms.Padding(10, 5, 5, 10);
this.groupBox3.Size = new System.Drawing.Size(404, 67);
this.groupBox3.TabIndex = 11;
this.groupBox3.TabStop = false;
//
// btBack180
//
this.btBack180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btBack180.Cursor = System.Windows.Forms.Cursors.Hand;
this.btBack180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btBack180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btBack180.ForeColor = System.Drawing.Color.Black;
this.btBack180.Location = new System.Drawing.Point(270, 19);
this.btBack180.Name = "btBack180";
this.btBack180.Size = new System.Drawing.Size(129, 38);
this.btBack180.TabIndex = 15;
this.btBack180.Text = "백후 180도 회전";
this.btBack180.UseVisualStyleBackColor = false;
this.btBack180.Click += new System.EventHandler(this.btBack180_Click);
//
// panel1
//
this.panel1.Dock = System.Windows.Forms.DockStyle.Left;
this.panel1.Location = new System.Drawing.Point(261, 19);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(9, 38);
this.panel1.TabIndex = 14;
//
// btRight180
//
this.btRight180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btRight180.Dock = System.Windows.Forms.DockStyle.Left;
this.btRight180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btRight180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btRight180.ForeColor = System.Drawing.Color.Black;
this.btRight180.Location = new System.Drawing.Point(140, 19);
this.btRight180.Location = new System.Drawing.Point(3, 62);
this.btRight180.Name = "btRight180";
this.btRight180.Size = new System.Drawing.Size(121, 38);
this.btRight180.Size = new System.Drawing.Size(196, 53);
this.btRight180.TabIndex = 6;
this.btRight180.Text = "180도 우회전";
this.btRight180.UseVisualStyleBackColor = false;
this.btRight180.Click += new System.EventHandler(this.btRight180_Click);
//
// panel10
//
this.panel10.Dock = System.Windows.Forms.DockStyle.Left;
this.panel10.Location = new System.Drawing.Point(131, 19);
this.panel10.Name = "panel10";
this.panel10.Size = new System.Drawing.Size(9, 38);
this.panel10.TabIndex = 12;
//
// btLeft180
//
this.btLeft180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btLeft180.Dock = System.Windows.Forms.DockStyle.Left;
this.btLeft180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btLeft180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btLeft180.ForeColor = System.Drawing.Color.Black;
this.btLeft180.Location = new System.Drawing.Point(10, 19);
this.btLeft180.Location = new System.Drawing.Point(3, 3);
this.btLeft180.Name = "btLeft180";
this.btLeft180.Size = new System.Drawing.Size(121, 38);
this.btLeft180.Size = new System.Drawing.Size(196, 53);
this.btLeft180.TabIndex = 7;
this.btLeft180.Text = "180도 좌회전";
this.btLeft180.UseVisualStyleBackColor = false;
@@ -484,7 +437,7 @@
this.button1.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.button1.Location = new System.Drawing.Point(0, 380);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(404, 70);
this.button1.Size = new System.Drawing.Size(404, 58);
this.button1.TabIndex = 10;
this.button1.Text = "설정 적용";
this.button1.UseVisualStyleBackColor = true;
@@ -506,25 +459,6 @@
this.groupBox1.TabStop = false;
this.groupBox1.Text = "근접센서(PBS)";
//
// radpbs2
//
this.radpbs2.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs2.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs2.BorderRadius = 7;
this.radpbs2.BorderSize = 2;
this.radpbs2.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs2.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs2.CheckWidth = 30;
this.radpbs2.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs2.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs2.Location = new System.Drawing.Point(278, 19);
this.radpbs2.Name = "radpbs2";
this.radpbs2.Size = new System.Drawing.Size(119, 66);
this.radpbs2.TabIndex = 10;
this.radpbs2.Text = "On(2)";
this.radpbs2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs2.UseVisualStyleBackColor = false;
//
// panel9
//
this.panel9.Dock = System.Windows.Forms.DockStyle.Left;
@@ -533,28 +467,6 @@
this.panel9.Size = new System.Drawing.Size(15, 66);
this.panel9.TabIndex = 12;
//
// radpbs1
//
this.radpbs1.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs1.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs1.BorderRadius = 7;
this.radpbs1.BorderSize = 2;
this.radpbs1.Checked = true;
this.radpbs1.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs1.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs1.CheckWidth = 30;
this.radpbs1.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs1.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs1.ForeColor = System.Drawing.Color.Blue;
this.radpbs1.Location = new System.Drawing.Point(144, 19);
this.radpbs1.Name = "radpbs1";
this.radpbs1.Size = new System.Drawing.Size(119, 66);
this.radpbs1.TabIndex = 9;
this.radpbs1.TabStop = true;
this.radpbs1.Text = "On(1)";
this.radpbs1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs1.UseVisualStyleBackColor = false;
//
// panel3
//
this.panel3.Dock = System.Windows.Forms.DockStyle.Left;
@@ -563,25 +475,6 @@
this.panel3.Size = new System.Drawing.Size(15, 66);
this.panel3.TabIndex = 11;
//
// radpbs0
//
this.radpbs0.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs0.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs0.BorderRadius = 7;
this.radpbs0.BorderSize = 2;
this.radpbs0.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs0.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs0.CheckWidth = 30;
this.radpbs0.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs0.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs0.Location = new System.Drawing.Point(10, 19);
this.radpbs0.Name = "radpbs0";
this.radpbs0.Size = new System.Drawing.Size(119, 66);
this.radpbs0.TabIndex = 8;
this.radpbs0.Text = "Off(0)";
this.radpbs0.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs0.UseVisualStyleBackColor = false;
//
// grpSpeed
//
this.grpSpeed.Controls.Add(this.radspdh);
@@ -598,25 +491,6 @@
this.grpSpeed.TabStop = false;
this.grpSpeed.Text = "속도";
//
// radspdh
//
this.radspdh.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdh.Bordercolor = System.Drawing.Color.DimGray;
this.radspdh.BorderRadius = 7;
this.radspdh.BorderSize = 2;
this.radspdh.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdh.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdh.CheckWidth = 30;
this.radspdh.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdh.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdh.Location = new System.Drawing.Point(278, 19);
this.radspdh.Name = "radspdh";
this.radspdh.Size = new System.Drawing.Size(119, 66);
this.radspdh.TabIndex = 2;
this.radspdh.Text = "고속";
this.radspdh.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdh.UseVisualStyleBackColor = false;
//
// panel8
//
this.panel8.Dock = System.Windows.Forms.DockStyle.Left;
@@ -625,28 +499,6 @@
this.panel8.Size = new System.Drawing.Size(15, 66);
this.panel8.TabIndex = 13;
//
// radspdm
//
this.radspdm.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdm.Bordercolor = System.Drawing.Color.DimGray;
this.radspdm.BorderRadius = 7;
this.radspdm.BorderSize = 2;
this.radspdm.Checked = true;
this.radspdm.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdm.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdm.CheckWidth = 30;
this.radspdm.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdm.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdm.ForeColor = System.Drawing.Color.Blue;
this.radspdm.Location = new System.Drawing.Point(144, 19);
this.radspdm.Name = "radspdm";
this.radspdm.Size = new System.Drawing.Size(119, 66);
this.radspdm.TabIndex = 1;
this.radspdm.TabStop = true;
this.radspdm.Text = "중속";
this.radspdm.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdm.UseVisualStyleBackColor = false;
//
// panel4
//
this.panel4.Dock = System.Windows.Forms.DockStyle.Left;
@@ -655,25 +507,6 @@
this.panel4.Size = new System.Drawing.Size(15, 66);
this.panel4.TabIndex = 12;
//
// radspdl
//
this.radspdl.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdl.Bordercolor = System.Drawing.Color.DimGray;
this.radspdl.BorderRadius = 7;
this.radspdl.BorderSize = 2;
this.radspdl.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdl.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdl.CheckWidth = 30;
this.radspdl.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdl.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdl.Location = new System.Drawing.Point(10, 19);
this.radspdl.Name = "radspdl";
this.radspdl.Size = new System.Drawing.Size(119, 66);
this.radspdl.TabIndex = 0;
this.radspdl.Text = "저속";
this.radspdl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdl.UseVisualStyleBackColor = false;
//
// grpBunki
//
this.grpBunki.Controls.Add(this.radright);
@@ -690,25 +523,6 @@
this.grpBunki.TabStop = false;
this.grpBunki.Text = "분기";
//
// radright
//
this.radright.BackColor = System.Drawing.Color.WhiteSmoke;
this.radright.Bordercolor = System.Drawing.Color.DimGray;
this.radright.BorderRadius = 7;
this.radright.BorderSize = 2;
this.radright.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radright.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radright.CheckWidth = 30;
this.radright.Dock = System.Windows.Forms.DockStyle.Left;
this.radright.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radright.Location = new System.Drawing.Point(278, 19);
this.radright.Name = "radright";
this.radright.Size = new System.Drawing.Size(119, 66);
this.radright.TabIndex = 4;
this.radright.Text = "우측";
this.radright.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radright.UseVisualStyleBackColor = false;
//
// panel7
//
this.panel7.Dock = System.Windows.Forms.DockStyle.Left;
@@ -717,28 +531,6 @@
this.panel7.Size = new System.Drawing.Size(15, 66);
this.panel7.TabIndex = 13;
//
// radstrai
//
this.radstrai.BackColor = System.Drawing.Color.WhiteSmoke;
this.radstrai.Bordercolor = System.Drawing.Color.DimGray;
this.radstrai.BorderRadius = 7;
this.radstrai.BorderSize = 2;
this.radstrai.Checked = true;
this.radstrai.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radstrai.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radstrai.CheckWidth = 30;
this.radstrai.Dock = System.Windows.Forms.DockStyle.Left;
this.radstrai.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radstrai.ForeColor = System.Drawing.Color.Blue;
this.radstrai.Location = new System.Drawing.Point(144, 19);
this.radstrai.Name = "radstrai";
this.radstrai.Size = new System.Drawing.Size(119, 66);
this.radstrai.TabIndex = 3;
this.radstrai.TabStop = true;
this.radstrai.Text = "직진";
this.radstrai.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radstrai.UseVisualStyleBackColor = false;
//
// panel5
//
this.panel5.Dock = System.Windows.Forms.DockStyle.Left;
@@ -747,25 +539,6 @@
this.panel5.Size = new System.Drawing.Size(15, 66);
this.panel5.TabIndex = 12;
//
// radleft
//
this.radleft.BackColor = System.Drawing.Color.WhiteSmoke;
this.radleft.Bordercolor = System.Drawing.Color.DimGray;
this.radleft.BorderRadius = 7;
this.radleft.BorderSize = 2;
this.radleft.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radleft.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radleft.CheckWidth = 30;
this.radleft.Dock = System.Windows.Forms.DockStyle.Left;
this.radleft.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radleft.Location = new System.Drawing.Point(10, 19);
this.radleft.Name = "radleft";
this.radleft.Size = new System.Drawing.Size(119, 66);
this.radleft.TabIndex = 5;
this.radleft.Text = "좌측";
this.radleft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radleft.UseVisualStyleBackColor = false;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.arLabel1);
@@ -837,6 +610,208 @@
this.panel12.Size = new System.Drawing.Size(15, 66);
this.panel12.TabIndex = 14;
//
// panel6
//
this.panel6.Dock = System.Windows.Forms.DockStyle.Left;
this.panel6.Location = new System.Drawing.Point(129, 19);
this.panel6.Name = "panel6";
this.panel6.Size = new System.Drawing.Size(15, 66);
this.panel6.TabIndex = 12;
//
// label1
//
this.label1.BackColor = System.Drawing.Color.Blue;
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(607, 577);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.label1.Size = new System.Drawing.Size(404, 38);
this.label1.TabIndex = 9;
this.label1.Text = "----------";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// radpbs2
//
this.radpbs2.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs2.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs2.BorderRadius = 7;
this.radpbs2.BorderSize = 2;
this.radpbs2.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs2.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs2.CheckWidth = 30;
this.radpbs2.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs2.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs2.Location = new System.Drawing.Point(278, 19);
this.radpbs2.Name = "radpbs2";
this.radpbs2.Size = new System.Drawing.Size(119, 66);
this.radpbs2.TabIndex = 10;
this.radpbs2.Text = "On(2)";
this.radpbs2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs2.UseVisualStyleBackColor = false;
//
// radpbs1
//
this.radpbs1.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs1.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs1.BorderRadius = 7;
this.radpbs1.BorderSize = 2;
this.radpbs1.Checked = true;
this.radpbs1.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs1.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs1.CheckWidth = 30;
this.radpbs1.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs1.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs1.ForeColor = System.Drawing.Color.Blue;
this.radpbs1.Location = new System.Drawing.Point(144, 19);
this.radpbs1.Name = "radpbs1";
this.radpbs1.Size = new System.Drawing.Size(119, 66);
this.radpbs1.TabIndex = 9;
this.radpbs1.TabStop = true;
this.radpbs1.Text = "On(1)";
this.radpbs1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs1.UseVisualStyleBackColor = false;
//
// radpbs0
//
this.radpbs0.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs0.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs0.BorderRadius = 7;
this.radpbs0.BorderSize = 2;
this.radpbs0.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs0.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs0.CheckWidth = 30;
this.radpbs0.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs0.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs0.Location = new System.Drawing.Point(10, 19);
this.radpbs0.Name = "radpbs0";
this.radpbs0.Size = new System.Drawing.Size(119, 66);
this.radpbs0.TabIndex = 8;
this.radpbs0.Text = "Off(0)";
this.radpbs0.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs0.UseVisualStyleBackColor = false;
//
// radspdh
//
this.radspdh.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdh.Bordercolor = System.Drawing.Color.DimGray;
this.radspdh.BorderRadius = 7;
this.radspdh.BorderSize = 2;
this.radspdh.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdh.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdh.CheckWidth = 30;
this.radspdh.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdh.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdh.Location = new System.Drawing.Point(278, 19);
this.radspdh.Name = "radspdh";
this.radspdh.Size = new System.Drawing.Size(119, 66);
this.radspdh.TabIndex = 2;
this.radspdh.Text = "고속";
this.radspdh.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdh.UseVisualStyleBackColor = false;
//
// radspdm
//
this.radspdm.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdm.Bordercolor = System.Drawing.Color.DimGray;
this.radspdm.BorderRadius = 7;
this.radspdm.BorderSize = 2;
this.radspdm.Checked = true;
this.radspdm.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdm.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdm.CheckWidth = 30;
this.radspdm.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdm.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdm.ForeColor = System.Drawing.Color.Blue;
this.radspdm.Location = new System.Drawing.Point(144, 19);
this.radspdm.Name = "radspdm";
this.radspdm.Size = new System.Drawing.Size(119, 66);
this.radspdm.TabIndex = 1;
this.radspdm.TabStop = true;
this.radspdm.Text = "중속";
this.radspdm.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdm.UseVisualStyleBackColor = false;
//
// radspdl
//
this.radspdl.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdl.Bordercolor = System.Drawing.Color.DimGray;
this.radspdl.BorderRadius = 7;
this.radspdl.BorderSize = 2;
this.radspdl.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdl.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdl.CheckWidth = 30;
this.radspdl.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdl.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdl.Location = new System.Drawing.Point(10, 19);
this.radspdl.Name = "radspdl";
this.radspdl.Size = new System.Drawing.Size(119, 66);
this.radspdl.TabIndex = 0;
this.radspdl.Text = "저속";
this.radspdl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdl.UseVisualStyleBackColor = false;
//
// radright
//
this.radright.BackColor = System.Drawing.Color.WhiteSmoke;
this.radright.Bordercolor = System.Drawing.Color.DimGray;
this.radright.BorderRadius = 7;
this.radright.BorderSize = 2;
this.radright.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radright.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radright.CheckWidth = 30;
this.radright.Dock = System.Windows.Forms.DockStyle.Left;
this.radright.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radright.Location = new System.Drawing.Point(278, 19);
this.radright.Name = "radright";
this.radright.Size = new System.Drawing.Size(119, 66);
this.radright.TabIndex = 4;
this.radright.Text = "우측";
this.radright.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radright.UseVisualStyleBackColor = false;
//
// radstrai
//
this.radstrai.BackColor = System.Drawing.Color.WhiteSmoke;
this.radstrai.Bordercolor = System.Drawing.Color.DimGray;
this.radstrai.BorderRadius = 7;
this.radstrai.BorderSize = 2;
this.radstrai.Checked = true;
this.radstrai.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radstrai.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radstrai.CheckWidth = 30;
this.radstrai.Dock = System.Windows.Forms.DockStyle.Left;
this.radstrai.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radstrai.ForeColor = System.Drawing.Color.Blue;
this.radstrai.Location = new System.Drawing.Point(144, 19);
this.radstrai.Name = "radstrai";
this.radstrai.Size = new System.Drawing.Size(119, 66);
this.radstrai.TabIndex = 3;
this.radstrai.TabStop = true;
this.radstrai.Text = "직진";
this.radstrai.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radstrai.UseVisualStyleBackColor = false;
//
// radleft
//
this.radleft.BackColor = System.Drawing.Color.WhiteSmoke;
this.radleft.Bordercolor = System.Drawing.Color.DimGray;
this.radleft.BorderRadius = 7;
this.radleft.BorderSize = 2;
this.radleft.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radleft.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radleft.CheckWidth = 30;
this.radleft.Dock = System.Windows.Forms.DockStyle.Left;
this.radleft.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radleft.Location = new System.Drawing.Point(10, 19);
this.radleft.Name = "radleft";
this.radleft.Size = new System.Drawing.Size(119, 66);
this.radleft.TabIndex = 5;
this.radleft.Text = "좌측";
this.radleft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radleft.UseVisualStyleBackColor = false;
//
// radbackward
//
this.radbackward.BackColor = System.Drawing.Color.WhiteSmoke;
@@ -856,14 +831,6 @@
this.radbackward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radbackward.UseVisualStyleBackColor = false;
//
// panel6
//
this.panel6.Dock = System.Windows.Forms.DockStyle.Left;
this.panel6.Location = new System.Drawing.Point(129, 19);
this.panel6.Name = "panel6";
this.panel6.Size = new System.Drawing.Size(15, 66);
this.panel6.TabIndex = 12;
//
// radforward
//
this.radforward.BackColor = System.Drawing.Color.WhiteSmoke;
@@ -886,20 +853,6 @@
this.radforward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radforward.UseVisualStyleBackColor = false;
//
// label1
//
this.label1.BackColor = System.Drawing.Color.Blue;
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(607, 538);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.label1.Size = new System.Drawing.Size(404, 38);
this.label1.TabIndex = 9;
this.label1.Text = "----------";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// guideSensor1
//
this.guideSensor1.Dock = System.Windows.Forms.DockStyle.Top;
@@ -912,11 +865,59 @@
this.guideSensor1.TabIndex = 8;
this.guideSensor1.Text = "guideSensor1";
//
// btBack180
//
this.btBack180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btBack180.Cursor = System.Windows.Forms.Cursors.Hand;
this.btBack180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btBack180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btBack180.ForeColor = System.Drawing.Color.Black;
this.btBack180.Location = new System.Drawing.Point(205, 3);
this.btBack180.Name = "btBack180";
this.btBack180.Size = new System.Drawing.Size(196, 53);
this.btBack180.TabIndex = 15;
this.btBack180.Text = "백후 180도 회전 (Left)";
this.btBack180.UseVisualStyleBackColor = false;
this.btBack180.Click += new System.EventHandler(this.btBack180_Click);
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Controls.Add(this.btBack180, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.btLeft180, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.btRight180, 0, 1);
this.tableLayoutPanel2.Controls.Add(this.button2, 1, 1);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 438);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 2;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(404, 118);
this.tableLayoutPanel2.TabIndex = 12;
//
// button2
//
this.button2.BackColor = System.Drawing.Color.WhiteSmoke;
this.button2.Cursor = System.Windows.Forms.Cursors.Hand;
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
this.button2.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.button2.ForeColor = System.Drawing.Color.Black;
this.button2.Location = new System.Drawing.Point(205, 62);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(196, 53);
this.button2.TabIndex = 15;
this.button2.Text = "백후 180도 회전(Right)";
this.button2.UseVisualStyleBackColor = false;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// fManual
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.SlateGray;
this.ClientSize = new System.Drawing.Size(1014, 579);
this.ClientSize = new System.Drawing.Size(1014, 618);
this.Controls.Add(this.panel2);
this.Controls.Add(this.label1);
this.Controls.Add(this.tableLayoutPanel1);
@@ -929,11 +930,11 @@
this.VisibleChanged += new System.EventHandler(this.fManual_VisibleChanged);
this.tableLayoutPanel1.ResumeLayout(false);
this.panel2.ResumeLayout(false);
this.groupBox3.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.grpSpeed.ResumeLayout(false);
this.grpBunki.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.ResumeLayout(false);
}
@@ -979,11 +980,10 @@
private System.Windows.Forms.Button button1;
private arCtl.arLabel arLabel1;
private System.Windows.Forms.Panel panel12;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.Button btBack180;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button btRight180;
private System.Windows.Forms.Panel panel10;
private System.Windows.Forms.Button btLeft180;
private System.Windows.Forms.Button btBack180;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.Button button2;
}
}

View File

@@ -291,7 +291,7 @@ namespace Project.ViewForm
if (radspdh.Checked) p.Speed = arDev.Narumi.eMoveSpd.High;
else if (radspdl.Checked) p.Speed = arDev.Narumi.eMoveSpd.Low;
else p.Speed = arDev.Narumi.eMoveSpd.Middle;
else p.Speed = arDev.Narumi.eMoveSpd.Mid;
if (radpbs1.Checked) p.PBSSensor = 2;
else if (radpbs2.Checked) p.PBSSensor = 2;
@@ -307,7 +307,7 @@ namespace Project.ViewForm
private void btChargeOn_Click(object sender, EventArgs e)
{
if (PUB.AGV.signal.mark_sensor == false)
if (VAR.BOOL[eVarBool.MARK_SENSOR] == false)
{
UTIL.MsgE("마크센서가 확인되지 않아 충전을 시작할 수 없습니다");
return;
@@ -331,7 +331,7 @@ namespace Project.ViewForm
}
private void arLabel1_Click_1(object sender, EventArgs e)
{
@@ -368,7 +368,12 @@ namespace Project.ViewForm
private void btBack180_Click(object sender, EventArgs e)
{
//[STX] C T B 0 0 0 0 9 9 [ETX]
PUB.AGV.AGVMoveBack180Turn();
PUB.AGV.AGVMoveBack180Turn(true);
}
private void button2_Click(object sender, EventArgs e)
{
PUB.AGV.AGVMoveBack180Turn(false);
}
}
}

View File

@@ -44,22 +44,6 @@ namespace Project
arFrame.Control.ColorListItem colorListItem9 = new arFrame.Control.ColorListItem();
arFrame.Control.ColorListItem colorListItem10 = new arFrame.Control.ColorListItem();
arFrame.Control.ColorListItem colorListItem11 = new arFrame.Control.ColorListItem();
Project.CtlPos.item item1 = new Project.CtlPos.item();
Project.CtlPos.item item2 = new Project.CtlPos.item();
Project.CtlPos.item item3 = new Project.CtlPos.item();
Project.CtlPos.item item4 = new Project.CtlPos.item();
Project.CtlPos.item item5 = new Project.CtlPos.item();
Project.CtlPos.item item6 = new Project.CtlPos.item();
Project.CtlPos.item item7 = new Project.CtlPos.item();
Project.CtlPos.item item8 = new Project.CtlPos.item();
Project.CtlPos.item item9 = new Project.CtlPos.item();
Project.CtlPos.item item10 = new Project.CtlPos.item();
Project.CtlPos.item item11 = new Project.CtlPos.item();
Project.CtlPos.item item12 = new Project.CtlPos.item();
Project.CtlPos.item item13 = new Project.CtlPos.item();
Project.CtlPos.item item14 = new Project.CtlPos.item();
Project.CtlPos.item item15 = new Project.CtlPos.item();
Project.CtlPos.item item16 = new Project.CtlPos.item();
this.tmDisplay = new System.Windows.Forms.Timer(this.components);
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.MenuLift = new System.Windows.Forms.Button();
@@ -125,7 +109,6 @@ namespace Project
this.lbIDLE = new arCtl.arLabel();
this.lbStStep = new arCtl.arLabel();
this.panTopMenu = new System.Windows.Forms.Panel();
this.lbBat = new AGVControl.BatteryLevelGauge();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.cmDebug = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mapFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -138,12 +121,12 @@ namespace Project
this.debugtestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.pandBottomDIO = new System.Windows.Forms.Panel();
this.panel9 = new System.Windows.Forms.Panel();
this.IOState = new arFrame.Control.GridView();
this.SSInfo = new arFrame.Control.GridView();
this.panDlg = new System.Windows.Forms.Panel();
this.arPanel2 = new arCtl.arPanel();
this.arPanel1 = new arCtl.arPanel();
this.ctlPos1 = new Project.CtlPos();
this.IOState = new arFrame.Control.GridView();
this.SSInfo = new arFrame.Control.GridView();
this.lbBat = new AGVControl.BatteryLevelGauge();
this.panRight.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.panel4.SuspendLayout();
@@ -299,7 +282,7 @@ namespace Project
this.btAutoRun.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btAutoRun.GradientRepeatBG = false;
this.btAutoRun.isButton = true;
this.btAutoRun.Location = new System.Drawing.Point(0, 288);
this.btAutoRun.Location = new System.Drawing.Point(0, 360);
this.btAutoRun.Margin = new System.Windows.Forms.Padding(0);
this.btAutoRun.MouseDownColor = System.Drawing.Color.Empty;
this.btAutoRun.MouseOverColor = System.Drawing.Color.Empty;
@@ -320,7 +303,7 @@ namespace Project
this.btAutoRun.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btAutoRun.SignColor = System.Drawing.Color.Yellow;
this.btAutoRun.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btAutoRun.Size = new System.Drawing.Size(259, 145);
this.btAutoRun.Size = new System.Drawing.Size(259, 180);
this.btAutoRun.TabIndex = 22;
this.btAutoRun.Text = "수동";
this.btAutoRun.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -367,7 +350,7 @@ namespace Project
this.btChargeA.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btChargeA.SignColor = System.Drawing.Color.Yellow;
this.btChargeA.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btChargeA.Size = new System.Drawing.Size(143, 144);
this.btChargeA.Size = new System.Drawing.Size(143, 180);
this.btChargeA.TabIndex = 141;
this.btChargeA.Text = "자동충전";
this.btChargeA.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -393,7 +376,7 @@ namespace Project
this.lbTime.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.lbTime.GradientRepeatBG = false;
this.lbTime.isButton = false;
this.lbTime.Location = new System.Drawing.Point(5, 574);
this.lbTime.Location = new System.Drawing.Point(5, 681);
this.lbTime.Margin = new System.Windows.Forms.Padding(0);
this.lbTime.MouseDownColor = System.Drawing.Color.Yellow;
this.lbTime.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -979,10 +962,10 @@ namespace Project
this.panRight.Controls.Add(this.panel3);
this.panRight.Controls.Add(this.panel1);
this.panRight.Dock = System.Windows.Forms.DockStyle.Right;
this.panRight.Location = new System.Drawing.Point(1015, 146);
this.panRight.Location = new System.Drawing.Point(1015, 58);
this.panRight.Name = "panRight";
this.panRight.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.panRight.Size = new System.Drawing.Size(264, 599);
this.panRight.Size = new System.Drawing.Size(264, 706);
this.panRight.TabIndex = 131;
//
// tableLayoutPanel1
@@ -1001,7 +984,7 @@ namespace Project
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(259, 433);
this.tableLayoutPanel1.Size = new System.Drawing.Size(259, 540);
this.tableLayoutPanel1.TabIndex = 0;
//
// btHome
@@ -1043,7 +1026,7 @@ namespace Project
this.btHome.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btHome.SignColor = System.Drawing.Color.Yellow;
this.btHome.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btHome.Size = new System.Drawing.Size(116, 288);
this.btHome.Size = new System.Drawing.Size(116, 360);
this.btHome.TabIndex = 141;
this.btHome.Text = "홈";
this.btHome.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1068,7 +1051,7 @@ namespace Project
this.btChargeM.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btChargeM.GradientRepeatBG = false;
this.btChargeM.isButton = true;
this.btChargeM.Location = new System.Drawing.Point(116, 144);
this.btChargeM.Location = new System.Drawing.Point(116, 180);
this.btChargeM.Margin = new System.Windows.Forms.Padding(0);
this.btChargeM.MouseDownColor = System.Drawing.Color.Yellow;
this.btChargeM.MouseOverColor = System.Drawing.Color.Lime;
@@ -1089,7 +1072,7 @@ namespace Project
this.btChargeM.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btChargeM.SignColor = System.Drawing.Color.Yellow;
this.btChargeM.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btChargeM.Size = new System.Drawing.Size(143, 144);
this.btChargeM.Size = new System.Drawing.Size(143, 180);
this.btChargeM.TabIndex = 141;
this.btChargeM.Text = "수동충전";
this.btChargeM.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1130,13 +1113,13 @@ namespace Project
this.lbCntQA.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.lbCntQA.Cursor = System.Windows.Forms.Cursors.Arrow;
this.lbCntQA.Dock = System.Windows.Forms.DockStyle.Left;
this.lbCntQA.Font = new System.Drawing.Font("Consolas", 25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntQA.Font = new System.Drawing.Font("Consolas", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntQA.ForeColor = System.Drawing.Color.Gray;
this.lbCntQA.GradientEnable = true;
this.lbCntQA.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.lbCntQA.GradientRepeatBG = false;
this.lbCntQA.isButton = false;
this.lbCntQA.Location = new System.Drawing.Point(161, 0);
this.lbCntQA.Location = new System.Drawing.Point(180, 0);
this.lbCntQA.Margin = new System.Windows.Forms.Padding(0);
this.lbCntQA.MouseDownColor = System.Drawing.Color.Yellow;
this.lbCntQA.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1157,7 +1140,7 @@ namespace Project
this.lbCntQA.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.lbCntQA.SignColor = System.Drawing.Color.Yellow;
this.lbCntQA.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.lbCntQA.Size = new System.Drawing.Size(93, 49);
this.lbCntQA.Size = new System.Drawing.Size(78, 49);
this.lbCntQA.TabIndex = 6;
this.lbCntQA.Text = "0000";
this.lbCntQA.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1175,13 +1158,13 @@ namespace Project
this.arLabel4.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.arLabel4.Cursor = System.Windows.Forms.Cursors.Arrow;
this.arLabel4.Dock = System.Windows.Forms.DockStyle.Left;
this.arLabel4.Font = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
this.arLabel4.Font = new System.Drawing.Font("Bahnschrift Condensed", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
this.arLabel4.GradientEnable = true;
this.arLabel4.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.arLabel4.GradientRepeatBG = false;
this.arLabel4.isButton = false;
this.arLabel4.Location = new System.Drawing.Point(127, 0);
this.arLabel4.Location = new System.Drawing.Point(129, 0);
this.arLabel4.Margin = new System.Windows.Forms.Padding(0);
this.arLabel4.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel4.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1201,10 +1184,10 @@ namespace Project
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", 15F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel4.Size = new System.Drawing.Size(34, 49);
this.arLabel4.SignFont = new System.Drawing.Font("Consolas", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel4.Size = new System.Drawing.Size(51, 49);
this.arLabel4.TabIndex = 5;
this.arLabel4.Text = "QA";
this.arLabel4.Text = "CHARGER";
this.arLabel4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel4.TextShadow = true;
this.arLabel4.TextVisible = true;
@@ -1220,13 +1203,13 @@ namespace Project
this.lbCntQC.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.lbCntQC.Cursor = System.Windows.Forms.Cursors.Arrow;
this.lbCntQC.Dock = System.Windows.Forms.DockStyle.Left;
this.lbCntQC.Font = new System.Drawing.Font("Consolas", 25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntQC.Font = new System.Drawing.Font("Consolas", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntQC.ForeColor = System.Drawing.Color.Gray;
this.lbCntQC.GradientEnable = true;
this.lbCntQC.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.lbCntQC.GradientRepeatBG = false;
this.lbCntQC.isButton = false;
this.lbCntQC.Location = new System.Drawing.Point(34, 0);
this.lbCntQC.Location = new System.Drawing.Point(51, 0);
this.lbCntQC.Margin = new System.Windows.Forms.Padding(0);
this.lbCntQC.MouseDownColor = System.Drawing.Color.Yellow;
this.lbCntQC.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1247,7 +1230,7 @@ namespace Project
this.lbCntQC.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.lbCntQC.SignColor = System.Drawing.Color.Yellow;
this.lbCntQC.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.lbCntQC.Size = new System.Drawing.Size(93, 49);
this.lbCntQC.Size = new System.Drawing.Size(78, 49);
this.lbCntQC.TabIndex = 4;
this.lbCntQC.Text = "0000";
this.lbCntQC.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1265,7 +1248,7 @@ namespace Project
this.arLabel9.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.arLabel9.Cursor = System.Windows.Forms.Cursors.Arrow;
this.arLabel9.Dock = System.Windows.Forms.DockStyle.Left;
this.arLabel9.Font = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel9.Font = new System.Drawing.Font("Bahnschrift Condensed", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel9.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
this.arLabel9.GradientEnable = true;
this.arLabel9.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
@@ -1291,10 +1274,10 @@ namespace Project
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", 15F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel9.Size = new System.Drawing.Size(34, 49);
this.arLabel9.SignFont = new System.Drawing.Font("Consolas", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel9.Size = new System.Drawing.Size(51, 49);
this.arLabel9.TabIndex = 4;
this.arLabel9.Text = "QC";
this.arLabel9.Text = "UNLOADER";
this.arLabel9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel9.TextShadow = true;
this.arLabel9.TextVisible = true;
@@ -1323,13 +1306,13 @@ namespace Project
this.lbCntDN.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.lbCntDN.Cursor = System.Windows.Forms.Cursors.Arrow;
this.lbCntDN.Dock = System.Windows.Forms.DockStyle.Left;
this.lbCntDN.Font = new System.Drawing.Font("Consolas", 25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntDN.Font = new System.Drawing.Font("Consolas", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCntDN.ForeColor = System.Drawing.Color.Gray;
this.lbCntDN.GradientEnable = true;
this.lbCntDN.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.lbCntDN.GradientRepeatBG = false;
this.lbCntDN.isButton = false;
this.lbCntDN.Location = new System.Drawing.Point(161, 0);
this.lbCntDN.Location = new System.Drawing.Point(180, 0);
this.lbCntDN.Margin = new System.Windows.Forms.Padding(0);
this.lbCntDN.MouseDownColor = System.Drawing.Color.Yellow;
this.lbCntDN.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1350,7 +1333,7 @@ namespace Project
this.lbCntDN.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.lbCntDN.SignColor = System.Drawing.Color.Yellow;
this.lbCntDN.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.lbCntDN.Size = new System.Drawing.Size(93, 49);
this.lbCntDN.Size = new System.Drawing.Size(78, 49);
this.lbCntDN.TabIndex = 4;
this.lbCntDN.Text = "0000";
this.lbCntDN.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1369,13 +1352,13 @@ namespace Project
this.arLabel7.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.arLabel7.Cursor = System.Windows.Forms.Cursors.Arrow;
this.arLabel7.Dock = System.Windows.Forms.DockStyle.Left;
this.arLabel7.Font = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel7.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
this.arLabel7.Font = new System.Drawing.Font("Bahnschrift Condensed", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel7.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(192)))), ((int)(((byte)(255)))));
this.arLabel7.GradientEnable = true;
this.arLabel7.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.arLabel7.GradientRepeatBG = false;
this.arLabel7.isButton = false;
this.arLabel7.Location = new System.Drawing.Point(127, 0);
this.arLabel7.Location = new System.Drawing.Point(129, 0);
this.arLabel7.Margin = new System.Windows.Forms.Padding(0);
this.arLabel7.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel7.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1395,10 +1378,10 @@ namespace Project
this.arLabel7.Sign = "";
this.arLabel7.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel7.SignColor = System.Drawing.Color.Yellow;
this.arLabel7.SignFont = new System.Drawing.Font("Consolas", 15F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel7.Size = new System.Drawing.Size(34, 49);
this.arLabel7.SignFont = new System.Drawing.Font("Consolas", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel7.Size = new System.Drawing.Size(51, 49);
this.arLabel7.TabIndex = 4;
this.arLabel7.Text = "DN";
this.arLabel7.Text = "CLEANNER";
this.arLabel7.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel7.TextShadow = true;
this.arLabel7.TextVisible = true;
@@ -1414,13 +1397,13 @@ namespace Project
this.lbCNtUP.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.lbCNtUP.Cursor = System.Windows.Forms.Cursors.Arrow;
this.lbCNtUP.Dock = System.Windows.Forms.DockStyle.Left;
this.lbCNtUP.Font = new System.Drawing.Font("Consolas", 25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCNtUP.Font = new System.Drawing.Font("Consolas", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbCNtUP.ForeColor = System.Drawing.Color.Gray;
this.lbCNtUP.GradientEnable = true;
this.lbCNtUP.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.lbCNtUP.GradientRepeatBG = false;
this.lbCNtUP.isButton = false;
this.lbCNtUP.Location = new System.Drawing.Point(34, 0);
this.lbCNtUP.Location = new System.Drawing.Point(51, 0);
this.lbCNtUP.Margin = new System.Windows.Forms.Padding(0);
this.lbCNtUP.MouseDownColor = System.Drawing.Color.Yellow;
this.lbCNtUP.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1441,7 +1424,7 @@ namespace Project
this.lbCNtUP.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.lbCNtUP.SignColor = System.Drawing.Color.Yellow;
this.lbCNtUP.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.lbCNtUP.Size = new System.Drawing.Size(93, 49);
this.lbCNtUP.Size = new System.Drawing.Size(78, 49);
this.lbCNtUP.TabIndex = 4;
this.lbCNtUP.Text = "0000";
this.lbCNtUP.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1460,7 +1443,7 @@ namespace Project
this.arLabel8.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.arLabel8.Cursor = System.Windows.Forms.Cursors.Arrow;
this.arLabel8.Dock = System.Windows.Forms.DockStyle.Left;
this.arLabel8.Font = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel8.Font = new System.Drawing.Font("Bahnschrift Condensed", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.arLabel8.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(255)))));
this.arLabel8.GradientEnable = true;
this.arLabel8.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
@@ -1486,10 +1469,10 @@ namespace Project
this.arLabel8.Sign = "";
this.arLabel8.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel8.SignColor = System.Drawing.Color.Yellow;
this.arLabel8.SignFont = new System.Drawing.Font("Consolas", 15F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel8.Size = new System.Drawing.Size(34, 49);
this.arLabel8.SignFont = new System.Drawing.Font("Consolas", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
this.arLabel8.Size = new System.Drawing.Size(51, 49);
this.arLabel8.TabIndex = 4;
this.arLabel8.Text = "UP";
this.arLabel8.Text = "LOADER";
this.arLabel8.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel8.TextShadow = true;
this.arLabel8.TextVisible = true;
@@ -1498,7 +1481,7 @@ namespace Project
//
this.panel5.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
this.panel5.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel5.Location = new System.Drawing.Point(5, 569);
this.panel5.Location = new System.Drawing.Point(5, 676);
this.panel5.Name = "panel5";
this.panel5.Size = new System.Drawing.Size(259, 5);
this.panel5.TabIndex = 142;
@@ -1639,26 +1622,6 @@ namespace Project
this.panTopMenu.Size = new System.Drawing.Size(1278, 50);
this.panTopMenu.TabIndex = 134;
//
// lbBat
//
this.lbBat.BorderColor = System.Drawing.Color.DimGray;
this.lbBat.CurA = 0F;
this.lbBat.Dock = System.Windows.Forms.DockStyle.Right;
this.lbBat.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbBat.ForeColor = System.Drawing.Color.Gray;
this.lbBat.IsOpen = true;
this.lbBat.Location = new System.Drawing.Point(830, 0);
this.lbBat.MaxA = 0F;
this.lbBat.Name = "lbBat";
this.lbBat.Padding = new System.Windows.Forms.Padding(0, 12, 0, 12);
this.lbBat.sign = "%";
this.lbBat.Size = new System.Drawing.Size(48, 50);
this.lbBat.TabIndex = 23;
this.lbBat.Text = "12";
this.lbBat.VLevel = 50F;
this.lbBat.Volt = 0F;
this.lbBat.Click += new System.EventHandler(this.lbBat_Click);
//
// pictureBox1
//
this.pictureBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
@@ -1752,7 +1715,7 @@ namespace Project
//
this.pandBottomDIO.Controls.Add(this.panel9);
this.pandBottomDIO.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pandBottomDIO.Location = new System.Drawing.Point(1, 745);
this.pandBottomDIO.Location = new System.Drawing.Point(1, 764);
this.pandBottomDIO.Margin = new System.Windows.Forms.Padding(0);
this.pandBottomDIO.Name = "pandBottomDIO";
this.pandBottomDIO.Size = new System.Drawing.Size(1278, 35);
@@ -1769,6 +1732,70 @@ namespace Project
this.panel9.Size = new System.Drawing.Size(1278, 35);
this.panel9.TabIndex = 0;
//
// panDlg
//
this.panDlg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(64)))));
this.panDlg.Dock = System.Windows.Forms.DockStyle.Fill;
this.panDlg.Location = new System.Drawing.Point(1, 58);
this.panDlg.Margin = new System.Windows.Forms.Padding(0);
this.panDlg.Name = "panDlg";
this.panDlg.Size = new System.Drawing.Size(1014, 706);
this.panDlg.TabIndex = 146;
//
// arPanel2
//
this.arPanel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel2.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel2.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel2.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel2.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel2.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel2.ForeColor = System.Drawing.Color.Khaki;
this.arPanel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel2.GradientRepeatBG = false;
this.arPanel2.Location = new System.Drawing.Point(1, 55);
this.arPanel2.Name = "arPanel2";
this.arPanel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel2.ProgressMax = 100F;
this.arPanel2.ProgressMin = 0F;
this.arPanel2.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel2.ProgressValue = 0F;
this.arPanel2.ShadowColor = System.Drawing.Color.Black;
this.arPanel2.ShowBorder = true;
this.arPanel2.Size = new System.Drawing.Size(1278, 3);
this.arPanel2.TabIndex = 145;
this.arPanel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel2.TextShadow = false;
this.arPanel2.UseProgressBar = false;
//
// arPanel1
//
this.arPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel1.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel1.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel1.ForeColor = System.Drawing.Color.Khaki;
this.arPanel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel1.GradientRepeatBG = false;
this.arPanel1.Location = new System.Drawing.Point(1, 51);
this.arPanel1.Name = "arPanel1";
this.arPanel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel1.ProgressMax = 100F;
this.arPanel1.ProgressMin = 0F;
this.arPanel1.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel1.ProgressValue = 0F;
this.arPanel1.ShadowColor = System.Drawing.Color.Black;
this.arPanel1.ShowBorder = true;
this.arPanel1.Size = new System.Drawing.Size(1278, 4);
this.arPanel1.TabIndex = 135;
this.arPanel1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel1.TextShadow = false;
this.arPanel1.UseProgressBar = false;
//
// IOState
//
this.IOState.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(50)))), ((int)(((byte)(50)))));
@@ -1921,291 +1948,35 @@ namespace Project
((ushort)(0))};
this.SSInfo.Click += new System.EventHandler(this.SSInfo_Click);
//
// panDlg
// lbBat
//
this.panDlg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(64)))));
this.panDlg.Dock = System.Windows.Forms.DockStyle.Fill;
this.panDlg.Location = new System.Drawing.Point(1, 146);
this.panDlg.Margin = new System.Windows.Forms.Padding(0);
this.panDlg.Name = "panDlg";
this.panDlg.Size = new System.Drawing.Size(1014, 599);
this.panDlg.TabIndex = 146;
//
// arPanel2
//
this.arPanel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel2.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel2.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel2.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel2.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel2.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel2.ForeColor = System.Drawing.Color.Khaki;
this.arPanel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel2.GradientRepeatBG = false;
this.arPanel2.Location = new System.Drawing.Point(1, 143);
this.arPanel2.Name = "arPanel2";
this.arPanel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel2.ProgressMax = 100F;
this.arPanel2.ProgressMin = 0F;
this.arPanel2.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel2.ProgressValue = 0F;
this.arPanel2.ShadowColor = System.Drawing.Color.Black;
this.arPanel2.ShowBorder = true;
this.arPanel2.Size = new System.Drawing.Size(1278, 3);
this.arPanel2.TabIndex = 145;
this.arPanel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel2.TextShadow = false;
this.arPanel2.UseProgressBar = false;
//
// arPanel1
//
this.arPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel1.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel1.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel1.ForeColor = System.Drawing.Color.Khaki;
this.arPanel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel1.GradientRepeatBG = false;
this.arPanel1.Location = new System.Drawing.Point(1, 51);
this.arPanel1.Name = "arPanel1";
this.arPanel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel1.ProgressMax = 100F;
this.arPanel1.ProgressMin = 0F;
this.arPanel1.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel1.ProgressValue = 0F;
this.arPanel1.ShadowColor = System.Drawing.Color.Black;
this.arPanel1.ShowBorder = true;
this.arPanel1.Size = new System.Drawing.Size(1278, 4);
this.arPanel1.TabIndex = 135;
this.arPanel1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel1.TextShadow = false;
this.arPanel1.UseProgressBar = false;
//
// ctlPos1
//
this.ctlPos1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.ctlPos1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.ctlPos1.Dock = System.Windows.Forms.DockStyle.Top;
this.ctlPos1.Font = new System.Drawing.Font("맑은 고딕", 30F, System.Drawing.FontStyle.Bold);
this.ctlPos1.ForeColor = System.Drawing.Color.White;
this.ctlPos1.ItemGap = 5;
item1.Active = true;
item1.Direction = '1';
item1.Enable_Direction = true;
item1.Focus = false;
item1.fullrect = new System.Drawing.Rectangle(8, 8, 10, 75);
item1.ItemType = Project.CtlPos.itemtype.NLimit;
item1.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item1.Position = Project.ePosition.NOT;
item1.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item1.Target = false;
item1.Title = "N";
item2.Active = false;
item2.Direction = '1';
item2.Enable_Direction = true;
item2.Focus = false;
item2.fullrect = new System.Drawing.Rectangle(23, 8, 138, 58);
item2.ItemType = Project.CtlPos.itemtype.Item;
item2.leftrect = new System.Drawing.Rectangle(23, 70, 67, 13);
item2.Position = Project.ePosition.QA;
item2.rightrect = new System.Drawing.Rectangle(94, 70, 67, 13);
item2.Target = false;
item2.Title = "QC";
item3.Active = false;
item3.Direction = '0';
item3.Enable_Direction = true;
item3.Focus = false;
item3.fullrect = new System.Drawing.Rectangle(166, 8, 10, 75);
item3.ItemType = Project.CtlPos.itemtype.Spacer;
item3.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item3.Position = Project.ePosition.QA_QC;
item3.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item3.Target = false;
item3.Title = null;
item4.Active = false;
item4.Direction = '1';
item4.Enable_Direction = true;
item4.Focus = false;
item4.fullrect = new System.Drawing.Rectangle(181, 8, 138, 58);
item4.ItemType = Project.CtlPos.itemtype.Item;
item4.leftrect = new System.Drawing.Rectangle(181, 70, 67, 13);
item4.Position = Project.ePosition.QC;
item4.rightrect = new System.Drawing.Rectangle(252, 70, 67, 13);
item4.Target = true;
item4.Title = "QA";
item5.Active = true;
item5.Direction = '0';
item5.Enable_Direction = false;
item5.Focus = true;
item5.fullrect = new System.Drawing.Rectangle(324, 8, 138, 75);
item5.ItemType = Project.CtlPos.itemtype.Item;
item5.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item5.Position = Project.ePosition.CHARGE;
item5.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item5.Target = false;
item5.Title = "BAT";
item6.Active = false;
item6.Direction = '0';
item6.Enable_Direction = true;
item6.Focus = false;
item6.fullrect = new System.Drawing.Rectangle(467, 8, 10, 75);
item6.ItemType = Project.CtlPos.itemtype.Spacer;
item6.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item6.Position = Project.ePosition.QC_F1;
item6.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item6.Target = false;
item6.Title = null;
item7.Active = false;
item7.Direction = '2';
item7.Enable_Direction = true;
item7.Focus = false;
item7.fullrect = new System.Drawing.Rectangle(482, 8, 138, 58);
item7.ItemType = Project.CtlPos.itemtype.Item;
item7.leftrect = new System.Drawing.Rectangle(482, 70, 67, 13);
item7.Position = Project.ePosition.F1;
item7.rightrect = new System.Drawing.Rectangle(553, 70, 67, 13);
item7.Target = false;
item7.Title = "#1";
item8.Active = false;
item8.Direction = '0';
item8.Enable_Direction = true;
item8.Focus = false;
item8.fullrect = new System.Drawing.Rectangle(625, 8, 10, 75);
item8.ItemType = Project.CtlPos.itemtype.Spacer;
item8.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item8.Position = Project.ePosition.F1_F2;
item8.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item8.Target = false;
item8.Title = null;
item9.Active = false;
item9.Direction = '0';
item9.Enable_Direction = true;
item9.Focus = false;
item9.fullrect = new System.Drawing.Rectangle(640, 8, 138, 58);
item9.ItemType = Project.CtlPos.itemtype.Item;
item9.leftrect = new System.Drawing.Rectangle(640, 70, 67, 13);
item9.Position = Project.ePosition.F2;
item9.rightrect = new System.Drawing.Rectangle(711, 70, 67, 13);
item9.Target = false;
item9.Title = "#2";
item10.Active = false;
item10.Direction = '0';
item10.Enable_Direction = true;
item10.Focus = false;
item10.fullrect = new System.Drawing.Rectangle(783, 8, 10, 75);
item10.ItemType = Project.CtlPos.itemtype.Spacer;
item10.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item10.Position = Project.ePosition.F2_F3;
item10.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item10.Target = false;
item10.Title = null;
item11.Active = false;
item11.Direction = '1';
item11.Enable_Direction = true;
item11.Focus = false;
item11.fullrect = new System.Drawing.Rectangle(798, 8, 138, 58);
item11.ItemType = Project.CtlPos.itemtype.Item;
item11.leftrect = new System.Drawing.Rectangle(798, 70, 67, 13);
item11.Position = Project.ePosition.F3;
item11.rightrect = new System.Drawing.Rectangle(869, 70, 67, 13);
item11.Target = false;
item11.Title = "#3";
item12.Active = false;
item12.Direction = '0';
item12.Enable_Direction = true;
item12.Focus = false;
item12.fullrect = new System.Drawing.Rectangle(941, 8, 10, 75);
item12.ItemType = Project.CtlPos.itemtype.Spacer;
item12.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item12.Position = Project.ePosition.F3_F4;
item12.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item12.Target = false;
item12.Title = null;
item13.Active = false;
item13.Direction = '2';
item13.Enable_Direction = true;
item13.Focus = false;
item13.fullrect = new System.Drawing.Rectangle(956, 8, 138, 58);
item13.ItemType = Project.CtlPos.itemtype.Item;
item13.leftrect = new System.Drawing.Rectangle(956, 70, 67, 13);
item13.Position = Project.ePosition.F4;
item13.rightrect = new System.Drawing.Rectangle(1027, 70, 67, 13);
item13.Target = false;
item13.Title = "#4";
item14.Active = false;
item14.Direction = '0';
item14.Enable_Direction = true;
item14.Focus = false;
item14.fullrect = new System.Drawing.Rectangle(1099, 8, 10, 75);
item14.ItemType = Project.CtlPos.itemtype.Spacer;
item14.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item14.Position = Project.ePosition.F4_F5;
item14.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item14.Target = false;
item14.Title = null;
item15.Active = false;
item15.Direction = '2';
item15.Enable_Direction = true;
item15.Focus = false;
item15.fullrect = new System.Drawing.Rectangle(1114, 8, 138, 58);
item15.ItemType = Project.CtlPos.itemtype.Item;
item15.leftrect = new System.Drawing.Rectangle(1114, 70, 67, 13);
item15.Position = Project.ePosition.F5;
item15.rightrect = new System.Drawing.Rectangle(1185, 70, 67, 13);
item15.Target = false;
item15.Title = "#5";
item16.Active = true;
item16.Direction = '1';
item16.Enable_Direction = true;
item16.Focus = false;
item16.fullrect = new System.Drawing.Rectangle(1257, 8, 10, 75);
item16.ItemType = Project.CtlPos.itemtype.PLimit;
item16.leftrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item16.Position = Project.ePosition.POT;
item16.rightrect = new System.Drawing.Rectangle(0, 0, 0, 0);
item16.Target = false;
item16.Title = "P";
this.ctlPos1.Items = new Project.CtlPos.item[] {
item1,
item2,
item3,
item4,
item5,
item6,
item7,
item8,
item9,
item10,
item11,
item12,
item13,
item14,
item15,
item16};
this.ctlPos1.Location = new System.Drawing.Point(1, 55);
this.ctlPos1.MinimumSize = new System.Drawing.Size(100, 30);
this.ctlPos1.msg = null;
this.ctlPos1.Name = "ctlPos1";
this.ctlPos1.Padding = new System.Windows.Forms.Padding(8);
this.ctlPos1.Size = new System.Drawing.Size(1278, 88);
this.ctlPos1.TabIndex = 0;
this.ctlPos1.Text = "ctlPos1";
this.ctlPos1.Visible = false;
this.lbBat.BorderColor = System.Drawing.Color.DimGray;
this.lbBat.CurA = 0F;
this.lbBat.Dock = System.Windows.Forms.DockStyle.Right;
this.lbBat.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbBat.ForeColor = System.Drawing.Color.Gray;
this.lbBat.IsOpen = true;
this.lbBat.Location = new System.Drawing.Point(830, 0);
this.lbBat.MaxA = 0F;
this.lbBat.Name = "lbBat";
this.lbBat.Padding = new System.Windows.Forms.Padding(0, 12, 0, 12);
this.lbBat.sign = "%";
this.lbBat.Size = new System.Drawing.Size(48, 50);
this.lbBat.TabIndex = 23;
this.lbBat.Text = "12";
this.lbBat.VLevel = 50F;
this.lbBat.Volt = 0F;
this.lbBat.Click += new System.EventHandler(this.lbBat_Click);
//
// fMain
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(15)))), ((int)(((byte)(15)))));
this.ClientSize = new System.Drawing.Size(1280, 781);
this.ClientSize = new System.Drawing.Size(1280, 800);
this.Controls.Add(this.panDlg);
this.Controls.Add(this.panRight);
this.Controls.Add(this.arPanel2);
this.Controls.Add(this.pandBottomDIO);
this.Controls.Add(this.ctlPos1);
this.Controls.Add(this.arPanel1);
this.Controls.Add(this.panTopMenu);
this.DoubleBuffered = true;
@@ -2297,7 +2068,6 @@ namespace Project
private System.Windows.Forms.Button MenuAuto;
private System.Windows.Forms.Panel panDlg;
private System.Windows.Forms.Button MenuLog;
private CtlPos ctlPos1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private arCtl.arLabel btHome;
private arCtl.arLabel btTopMenu_Volume;

View File

@@ -39,6 +39,7 @@ namespace Project
InitializeComponent();
VAR.Init(128);
PUB.initCore();
UTIL.MessgeBoxLegacyMode = false;
this.KeyDown += (s1, e1) =>
{
if (e1.KeyCode == Keys.Escape) this.Close();
@@ -65,7 +66,6 @@ namespace Project
this.panTopMenu.MouseDown += LbTitle_MouseDown;
this.panTopMenu.DoubleClick += LbTitle_DoubleClick;
this.ctlPos1.ItemClick += CtlPos1_ItemClick;
this.MouseMove += (s1, e1) => { if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now; };
this.FormClosing += __Closing;
@@ -100,19 +100,6 @@ namespace Project
}
}
/// <summary>
/// 상단 위치 표시기의 대상 위치값을 표시 합니다
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Display_Position_TargetPosSet(object sender, EventArgs e)
{
//대상위치가 설정되었다x
this.ctlPos1.SetTargetPosition(PUB.Result.TargetPos);
this.ctlPos1.Invalidate();
}
private void __Closing(object sender, FormClosingEventArgs e)
{
// 장치 관리 태스크는 _STEP_CLOSING_START에서 종료됨
@@ -175,50 +162,31 @@ namespace Project
private void __Load(object sender, EventArgs e)
{
this.Text = Application.ProductName + " ver " + Application.ProductVersion;
//this.lbTitle.Text = this.Text;
PUB.init(); //public initialize
if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized;
// PUB.log.RaiseMsg += log_RaiseMsg;
PUB.init();
if (PUB.setting.FullScreen)
{
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
}
this.Show();
PUB.Result.TargetPosSet += Display_Position_TargetPosSet;
this.ctlPos1.ClearData();
this.ctlPos1.Invalidate();
//lbDM1.Text = "";
//sbBatteryLv.Text = "";
btHome.Text = "홈";
btChargeA.Text = "자동충전";
VAR.STR[eVarString.SWVersion] = Application.ProductVersion;
Application.DoEvents();
//setting dio events
VAR.STR[eVarString.SWVersion] = Application.ProductVersion;
//SETTING H/W
this.IOState.ItemClick += gridView2_ItemClick;
//PUB.flag.ValueChanged += Flag_ValueChagned;
VAR.BOOL.PropertyChanged += BOOL_PropertyChanged;
/////모터용 pLC
//PUB.PLC = new arDev.FakePLC();
//PUB.PLC.ValueChanged += PLC_DioChanged;
//PUB.PLC.FlagChanged += PLC_FlagChanged;
//PUB.PLC.Message += PLC_Message;
//지그비통신
PUB.XBE = new Device.Xbee();
PUB.XBE.MessageReceived += XBE_MessageReceived;
PUB.XBE.ProtocReceived += XBE_ProtocReceived;
//HWState.setTitle(1, 3, Pub.setting.Port_Xbee);
//HWState.setTitle(1, 0, Pub.setting.Address_RFID);
//AGV
PUB.AGV = new arDev.Narumi();
PUB.AGV.Message += AGV_Message;
PUB.AGV.DataReceive += AGV_DataReceive;
//배터리관리시스템
PUB.BMS = new arDev.BMS();
PUB.BMS.BMSDataReceive += Bms_BMSDataReceive;
@@ -243,17 +211,17 @@ namespace Project
PUB.sm.SetMsgOptOff(); //모든 메세지출력을 해제한다. (이벤트는 동작함)
PUB.log.Add("State Machine Start");
try
{
PUB.sm = new StateMachine.StateMachine();
PUB.log.Add("StateMachine", $"객체 생성 완료 - Type: {PUB.sm.GetType().FullName}");
// StateMachine 객체의 속성 확인
var smType = PUB.sm.GetType();
var properties = smType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
PUB.log.Add("StateMachine", $"Public Properties: {properties.Length}개");
var methods = smType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
PUB.log.Add("StateMachine", $"Public Methods: {methods.Length}개");
}
@@ -263,22 +231,14 @@ namespace Project
PUB.log.AddE($"StackTrace: {ex.StackTrace}");
throw;
}
PUB.sm.StepChanged += sm_StepChanged;
PUB.log.Add("StateMachine", "StepChanged 이벤트 등록 완료");
PUB.sm.Message += sm_Message;
PUB.log.Add("StateMachine", "Message 이벤트 등록 완료");
PUB.sm.Running += sm_Running;
PUB.log.Add("StateMachine", "Running 이벤트 등록 완료");
PUB.sm.SPS += sm_SPS;
PUB.log.Add("StateMachine", "SPS 이벤트 등록 완료");
PUB.sm.Start();
PUB.log.Add("StateMachine", $"Start() 호출 완료 - IsThreadRun:{PUB.sm.IsThreadRun}");
PUB.log.Add("StateMachine", "SM 이벤트 등록 완료");
// 스레드 시작 대기 (최대 3초)
for (int i = 0; i < 30; i++)
{
@@ -289,15 +249,15 @@ namespace Project
break;
}
}
if (!PUB.sm.IsThreadRun)
{
PUB.log.AddE( "경고: 3초 대기 후에도 스레드가 시작되지 않음!");
PUB.log.AddE("경고: 3초 대기 후에도 스레드가 시작되지 않음!");
System.Windows.Forms.MessageBox.Show(
"상태머신 스레드가 시작되지 않았습니다.\n" +
"로그 파일을 확인하세요.",
"오류",
System.Windows.Forms.MessageBoxButtons.OK,
"오류",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Error);
}
@@ -306,10 +266,8 @@ namespace Project
PUB.log.Add("Display", "Display Timer 시작 완료");
this.btDebug.Visible = System.Diagnostics.Debugger.IsAttached || PUB.setting.UseDebugMode;
PUB.log.Add("Program Start");
//수량표시
PUB.counter.PropertyChanged += (s1, e1) => Update_Count();
Update_Count();
@@ -321,11 +279,8 @@ namespace Project
MenuLog.PerformClick();
PUB.AddEEDB("프로그램 시작");
}
#region "Mouse Form Move"
private Boolean fMove = false;
@@ -416,6 +371,11 @@ namespace Project
void func_sw_start(bool Prompt = false)
{
if (PUB.sm.Step == eSMStep.SYNC)
{
UTIL.MsgE("초기화 중에는 사용할 수 없습니다\n초기화가 완료 된 후 시도하세요");
return;
}
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) //자동상태가 아니라면
{
PUB.AGV.AGVErrorReset();
@@ -423,17 +383,17 @@ namespace Project
{
if (UTIL.MsgQ("AGV상태를 자동으로 전환 할까요?") != DialogResult.Yes) return;
}
//충전상태확인
if (PUB.CheckManualChargeMode() == false) return;
PUB.popup.needClose = true;
PUB.sm.bPause = false;
//PUB.sm.bPause = false;
//PUB.sm.ClearRunStep();
//PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.sm.SetNewStep(eSMStep.RUN);
PUB.Speak(Lang., addlog: false);
if (PUB.Result.CurrentPos == ePosition.NONE || PUB.Result.TargetPos == ePosition.NONE)
{
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
}
PUB.Speak(Lang.);
PUB.log.Add($"자동전환 이전스텝:{PUB.sm.RunStep},IDX:{PUB.sm.RunStepSeq}]");
}
else
{
@@ -487,159 +447,6 @@ namespace Project
}
private void CtlPos1_ItemClick(object sender, CtlPos.ItemClickEventArgs e)
{
if (VAR.BOOL[eVarBool.FLAG_CHARGEONM] == true)
{
UTIL.MsgE("수동 충전 상태이므로 진행 할 수 없습니다");
return;
}
if (VAR.BOOL[eVarBool.EMERGENCY] == true)
{
var dlgE = UTIL.MsgQ("비상정지 상태입니다.\n오류를 먼저 소거하고 실행 할까요?");
if (dlgE == DialogResult.Yes) PUB.AGV.AGVErrorReset();
else return;
}
//위치표시 컨트롤에서 아이템을 클릭했다
var dlg = new Dialog.fJobSelect();
PUB.log.Add("사용자 임의 위치 클릭 : " + e.Item.Title);
switch (e.Item.Position)
{
case ePosition.QA:
//아이템을 가지고 있었으니 하차를 해야한다
if (VAR.BOOL[eVarBool.ITEMON])
{
dlg.setMessage("작업 실행\n" +
"(QA) 위치로 하차를 진행 할까요?\n" +
"대상위치 : QA\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
//아이템이 있으면 하차이고 없으면 상차이다
PUB.sm.ClearRunStep();
PUB.Result.TargetPos = ePosition.QA;
PUB.sm.SetNewRunStep(ERunStep.GODOWN);
PUB.sm.SetNewStep(eSMStep.RUN);
}
}
else
{
dlg.setMessage("작업 실행\n" +
"(QA) 위치로 이동을 실행 할까요?\n" +
"대상위치 : QA\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
//아이템이 있으면 하차이고 없으면 상차이다
PUB.sm.ClearRunStep();
PUB.Result.TargetPos = ePosition.QA;
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
PUB.sm.SetNewStep(eSMStep.RUN);
}
}
break;
case ePosition.QC:
//아이템을 가지고 있었으니 하차를 해야한다
if (VAR.BOOL[eVarBool.ITEMON])
{
dlg.setMessage("작업 실행\n" +
"홈(QC) 위치로 하차를 진행 할까요?\n" +
"대상위치 : QC\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
//아이템이 있으면 하차이고 없으면 상차이다
PUB.sm.ClearRunStep();
PUB.Result.TargetPos = ePosition.QC;
PUB.sm.SetNewRunStep(ERunStep.GODOWN);
PUB.sm.SetNewStep(eSMStep.RUN);
}
}
else
{
dlg.setMessage("작업 실행\n" +
"홈(QC) 위치로 이동을 실행 할까요?\n" +
"대상위치 : QC\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
//아이템이 있으면 하차이고 없으면 상차이다
PUB.sm.ClearRunStep();
PUB.Result.TargetPos = ePosition.QC;
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
PUB.sm.SetNewStep(eSMStep.RUN);
}
}
break;
case ePosition.F1: //FVI영역은 모두 상차이동
dlg.setMessage("작업 실행\n" +
"(상차) 작업을 실행 할까요?\n" +
"대상위치 : FVI-1\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
PUB.Result.TargetPos = ePosition.F1;
PUB.sm.SetNewRunStep(ERunStep.GOUP);
PUB.sm.SetNewStep(eSMStep.RUN);
}
break;
case ePosition.F2:
dlg.setMessage("작업 실행\n" +
"(상차) 작업을 실행 할까요?\n" +
"대상위치 : FVI-2\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
PUB.Result.TargetPos = ePosition.F2;
PUB.sm.SetNewRunStep(ERunStep.GOUP);
PUB.sm.SetNewStep(eSMStep.RUN);
}
break;
case ePosition.F3:
dlg.setMessage("작업 실행\n" +
"(상차) 작업을 실행 할까요?\n" +
"대상위치 : FVI-3\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
PUB.Result.TargetPos = ePosition.F3;
PUB.sm.SetNewRunStep(ERunStep.GOUP);
PUB.sm.SetNewStep(eSMStep.RUN);
//VAR.BOOL[eVarBool.FLAG_AUTORUN] = true;
}
break;
case ePosition.F4:
dlg.setMessage("작업 실행\n" +
"(상차) 작업을 실행 할까요?\n" +
"대상위치 : FVI-4\n" +
"현재위치 : " + PUB.Result.CurrentPos.ToString());
if (dlg.ShowDialog() == DialogResult.Yes)
{
PUB.Result.TargetPos = ePosition.F4;
PUB.sm.SetNewRunStep(ERunStep.GOUP);
PUB.sm.SetNewStep(eSMStep.RUN);
}
break;
}
if (dlg != null) dlg.Dispose();
}
private void demoRunToolStripMenuItem_Click(object sender, EventArgs e)
{
PUB.Result.JobEndTime = DateTime.Now;
@@ -808,7 +615,10 @@ namespace Project
if (dlg == DialogResult.Yes)
{
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.GOHOME);
//대상노드를 충전기로하고
PUB._virtualAGV.StartNode = null;
PUB._virtualAGV.TargetNode = null;
PUB.sm.SetNewRunStep(ERunStep.GOTO);
PUB.sm.SetNewStep(eSMStep.RUN);
PUB.log.Add("사용자 충전 해제");
}
@@ -847,26 +657,23 @@ namespace Project
}
private void brHome_Click(object sender, EventArgs e)
{
var bCharge = PUB.sm.Step == eSMStep.RUN &&
(PUB.sm.RunStep == ERunStep.GOHOME);
if (PUB.CheckManualChargeMode() == false) return;
if (bCharge == true)
var bHome = PUB.sm.Step == eSMStep.RUN && (PUB.sm.RunStep == ERunStep.GOHOME);
if (bHome == true)
{
var dlg = UTIL.MsgQ("홈(QC) 이동을 취소 할까요?");
var dlg = UTIL.MsgQ("홈 이동을 취소 할까요?");
if (dlg == DialogResult.Yes)
{
PUB.sm.ClearRunStep();
PUB.sm.SetNewStep(eSMStep.IDLE);
PUB.AGV.AGVMoveStop("user home cancle", arDev.Narumi.eStopOpt.Stop);
//PUB.AGV.AddCommand(arDev.Narumi.eAgvCmd.MoveStop);//.Move(Device.PLC.Rundirection.Stop, "사용자 홈 이동 취소");
PUB.log.Add("사용자 홈 이동 취소");
}
}
else
{
var dlg = UTIL.MsgQ("홈(QC) 이동을 실행 할까요?");
var dlg = UTIL.MsgQ("홈 이동을 실행 할까요?");
if (dlg == DialogResult.Yes)
{
PUB.sm.ClearRunStep();
@@ -875,7 +682,6 @@ namespace Project
PUB.log.Add("사용자 홈 이동 실행");
}
}
}
@@ -967,7 +773,7 @@ namespace Project
private void loadToolStripMenuItem_Click(object sender, EventArgs e)
{
//load
var od = new OpenFileDialog
{
Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*",
@@ -996,6 +802,7 @@ namespace Project
{
var _mapCanvas = PUB._mapCanvas;
PUB._mapNodes = result.Nodes;
PUB.log.Add($"Set _mapNodes");
// 맵 캔버스에 데이터 설정
_mapCanvas.Nodes = result.Nodes;
@@ -1084,11 +891,11 @@ namespace Project
sb.AppendLine($"Current Step: {PUB.sm.Step}");
sb.AppendLine($"Current RunStep: {PUB.sm.RunStep}");
sb.AppendLine();
var process = System.Diagnostics.Process.GetCurrentProcess();
sb.AppendLine($"총 스레드 수: {process.Threads.Count}");
sb.AppendLine();
foreach (System.Diagnostics.ProcessThread thread in process.Threads)
{
sb.AppendLine($"Thread {thread.Id}:");
@@ -1100,11 +907,11 @@ namespace Project
}
sb.AppendLine();
}
var dump = sb.ToString();
PUB.log.Add("THREAD_DUMP", dump);
Console.WriteLine(dump);
MessageBox.Show(
$"스레드 덤프 완료\n로그 파일에 저장되었습니다.\n\n" +
//$"마지막 sm_Running: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전\n" +

View File

@@ -39,6 +39,10 @@
this.btAutoCharge = new arCtl.arLabel();
this.btSpeaker = new arCtl.arLabel();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.button8 = new System.Windows.Forms.Button();
this.vcGDS = new AGVControl.ValueSelect();
this.label7 = new System.Windows.Forms.Label();
this.vcXBID = new AGVControl.ValueSelect();
this.tbagvchannel = new System.Windows.Forms.TextBox();
this.tbagvpanid = new System.Windows.Forms.TextBox();
this.tbagvaddr = new System.Windows.Forms.TextBox();
@@ -171,7 +175,6 @@
this.tbMCID = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.chkDetectManualCharge = new System.Windows.Forms.CheckBox();
this.chkAGVDirBack = new System.Windows.Forms.CheckBox();
this.chkFullScreen = new System.Windows.Forms.CheckBox();
this.nudMusicVol = new System.Windows.Forms.NumericUpDown();
this.label22 = new System.Windows.Forms.Label();
@@ -202,8 +205,6 @@
this.bt1 = new arCtl.arLabel();
this.bt0 = new arCtl.arLabel();
this.btSave = new arCtl.arLabel();
this.label7 = new System.Windows.Forms.Label();
this.vcXBID = new AGVControl.ValueSelect();
this.tabControl1.SuspendLayout();
this.tabPage6.SuspendLayout();
this.panel1.SuspendLayout();
@@ -249,7 +250,7 @@
this.tabControl1.Multiline = true;
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(1270, 598);
this.tabControl1.Size = new System.Drawing.Size(1270, 578);
this.tabControl1.TabIndex = 14;
//
// tabPage6
@@ -257,7 +258,7 @@
this.tabPage6.Controls.Add(this.panel1);
this.tabPage6.Location = new System.Drawing.Point(4, 4);
this.tabPage6.Name = "tabPage6";
this.tabPage6.Size = new System.Drawing.Size(1244, 590);
this.tabPage6.Size = new System.Drawing.Size(1244, 570);
this.tabPage6.TabIndex = 6;
this.tabPage6.Text = "일반";
this.tabPage6.UseVisualStyleBackColor = true;
@@ -274,19 +275,21 @@
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
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.btMusic, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.btAutoCharge, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.btSpeaker, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 0);
this.tableLayoutPanel1.Controls.Add(this.btMusic, 0, 1);
this.tableLayoutPanel1.Location = new System.Drawing.Point(17, 17);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(1238, 648);
this.tableLayoutPanel1.Size = new System.Drawing.Size(1227, 550);
this.tableLayoutPanel1.TabIndex = 24;
//
// btMusic
@@ -306,7 +309,7 @@
this.btMusic.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.btMusic.GradientRepeatBG = false;
this.btMusic.isButton = true;
this.btMusic.Location = new System.Drawing.Point(3, 327);
this.btMusic.Location = new System.Drawing.Point(3, 278);
this.btMusic.MouseDownColor = System.Drawing.Color.Yellow;
this.btMusic.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btMusic.msg = null;
@@ -326,7 +329,7 @@
this.btMusic.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btMusic.SignColor = System.Drawing.Color.Yellow;
this.btMusic.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btMusic.Size = new System.Drawing.Size(613, 318);
this.btMusic.Size = new System.Drawing.Size(607, 269);
this.btMusic.TabIndex = 23;
this.btMusic.Tag = "2";
this.btMusic.Text = "배경음악";
@@ -352,7 +355,7 @@
this.btAutoCharge.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.btAutoCharge.GradientRepeatBG = false;
this.btAutoCharge.isButton = true;
this.btAutoCharge.Location = new System.Drawing.Point(622, 3);
this.btAutoCharge.Location = new System.Drawing.Point(616, 3);
this.btAutoCharge.MouseDownColor = System.Drawing.Color.Yellow;
this.btAutoCharge.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btAutoCharge.msg = null;
@@ -372,7 +375,7 @@
this.btAutoCharge.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btAutoCharge.SignColor = System.Drawing.Color.Yellow;
this.btAutoCharge.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btAutoCharge.Size = new System.Drawing.Size(613, 318);
this.btAutoCharge.Size = new System.Drawing.Size(608, 269);
this.btAutoCharge.TabIndex = 20;
this.btAutoCharge.Tag = "1";
this.btAutoCharge.Text = "자동충전";
@@ -418,7 +421,7 @@
this.btSpeaker.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btSpeaker.SignColor = System.Drawing.Color.Yellow;
this.btSpeaker.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btSpeaker.Size = new System.Drawing.Size(613, 318);
this.btSpeaker.Size = new System.Drawing.Size(607, 269);
this.btSpeaker.TabIndex = 11;
this.btSpeaker.Tag = "0";
this.btSpeaker.Text = "음성지원";
@@ -430,6 +433,8 @@
// tabPage1
//
this.tabPage1.BackColor = System.Drawing.Color.Navy;
this.tabPage1.Controls.Add(this.button8);
this.tabPage1.Controls.Add(this.vcGDS);
this.tabPage1.Controls.Add(this.label7);
this.tabPage1.Controls.Add(this.vcXBID);
this.tabPage1.Controls.Add(this.tbagvchannel);
@@ -477,10 +482,81 @@
this.tabPage1.Location = new System.Drawing.Point(4, 4);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(1244, 590);
this.tabPage1.Size = new System.Drawing.Size(1244, 570);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "AGV";
//
// button8
//
this.button8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.button8.Font = new System.Drawing.Font("궁서체", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.button8.Location = new System.Drawing.Point(480, 509);
this.button8.Name = "button8";
this.button8.Size = new System.Drawing.Size(147, 54);
this.button8.TabIndex = 82;
this.button8.Tag = "SCH";
this.button8.Text = "TurnGDS";
this.button8.UseVisualStyleBackColor = true;
this.button8.Click += new System.EventHandler(this.button8_Click);
//
// vcGDS
//
this.vcGDS.BackColorButton = System.Drawing.Color.White;
this.vcGDS.ButtonWidth = "30";
this.vcGDS.ColorBorder = System.Drawing.Color.White;
this.vcGDS.DecimalPosition = ((ushort)(0));
this.vcGDS.Font = new System.Drawing.Font("맑은 고딕", 26.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.vcGDS.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.vcGDS.ForeColor = System.Drawing.Color.White;
this.vcGDS.ForeColorButton = System.Drawing.Color.Black;
this.vcGDS.Location = new System.Drawing.Point(633, 509);
this.vcGDS.MaxValue = 9999D;
this.vcGDS.MinValue = 0D;
this.vcGDS.Name = "vcGDS";
this.vcGDS.NullDisplay = "--";
this.vcGDS.SideButtonClickValue = 1D;
this.vcGDS.Size = new System.Drawing.Size(164, 54);
this.vcGDS.TabIndex = 80;
this.vcGDS.Tag = "SDH";
this.vcGDS.Text = "1000";
this.vcGDS.Value = 1000D;
this.vcGDS.ButtonClick += new System.EventHandler<AGVControl.ValueSelect.ButtonClickEventArgs>(this.vcChargeLow_ButtonClick);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label7.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label7.Location = new System.Drawing.Point(162, 524);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(101, 24);
this.label7.TabIndex = 79;
this.label7.Text = "Xbee ID";
this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// vcXBID
//
this.vcXBID.BackColorButton = System.Drawing.Color.White;
this.vcXBID.ButtonWidth = "30";
this.vcXBID.ColorBorder = System.Drawing.Color.White;
this.vcXBID.DecimalPosition = ((ushort)(0));
this.vcXBID.Font = new System.Drawing.Font("맑은 고딕", 26.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.vcXBID.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.vcXBID.ForeColor = System.Drawing.Color.White;
this.vcXBID.ForeColorButton = System.Drawing.Color.Black;
this.vcXBID.Location = new System.Drawing.Point(269, 509);
this.vcXBID.MaxValue = 9999D;
this.vcXBID.MinValue = 0D;
this.vcXBID.Name = "vcXBID";
this.vcXBID.NullDisplay = "--";
this.vcXBID.SideButtonClickValue = 1D;
this.vcXBID.Size = new System.Drawing.Size(164, 54);
this.vcXBID.TabIndex = 78;
this.vcXBID.Tag = "SDH";
this.vcXBID.Text = "30";
this.vcXBID.Value = 30D;
this.vcXBID.ButtonClick += new System.EventHandler<AGVControl.ValueSelect.ButtonClickEventArgs>(this.vcChargeLow_ButtonClick);
//
// tbagvchannel
//
this.tbagvchannel.Font = new System.Drawing.Font("맑은 고딕", 26.25F, System.Drawing.FontStyle.Bold);
@@ -1565,7 +1641,7 @@
this.label44.AutoSize = true;
this.label44.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label44.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label44.Location = new System.Drawing.Point(473, 546);
this.label44.Location = new System.Drawing.Point(1152, 201);
this.label44.Name = "label44";
this.label44.Size = new System.Drawing.Size(49, 24);
this.label44.TabIndex = 64;
@@ -1576,7 +1652,7 @@
this.label29.AutoSize = true;
this.label29.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label29.ForeColor = System.Drawing.Color.SkyBlue;
this.label29.Location = new System.Drawing.Point(51, 546);
this.label29.Location = new System.Drawing.Point(730, 201);
this.label29.Name = "label29";
this.label29.Size = new System.Drawing.Size(211, 24);
this.label29.TabIndex = 62;
@@ -1631,7 +1707,7 @@
this.label28.AutoSize = true;
this.label28.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label28.ForeColor = System.Drawing.Color.SkyBlue;
this.label28.Location = new System.Drawing.Point(138, 503);
this.label28.Location = new System.Drawing.Point(817, 158);
this.label28.Name = "label28";
this.label28.Size = new System.Drawing.Size(124, 24);
this.label28.TabIndex = 29;
@@ -1838,7 +1914,7 @@
this.vcChargeWaitSec.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.vcChargeWaitSec.ForeColor = System.Drawing.Color.WhiteSmoke;
this.vcChargeWaitSec.ForeColorButton = System.Drawing.Color.Black;
this.vcChargeWaitSec.Location = new System.Drawing.Point(270, 540);
this.vcChargeWaitSec.Location = new System.Drawing.Point(949, 195);
this.vcChargeWaitSec.MaxValue = 999999D;
this.vcChargeWaitSec.MinValue = 0D;
this.vcChargeWaitSec.Name = "vcChargeWaitSec";
@@ -1862,7 +1938,7 @@
this.tbChargerID.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tbChargerID.ForeColor = System.Drawing.Color.WhiteSmoke;
this.tbChargerID.ForeColorButton = System.Drawing.Color.Black;
this.tbChargerID.Location = new System.Drawing.Point(270, 497);
this.tbChargerID.Location = new System.Drawing.Point(949, 152);
this.tbChargerID.MaxValue = 999999D;
this.tbChargerID.MinValue = 0D;
this.tbChargerID.Name = "tbChargerID";
@@ -2654,7 +2730,6 @@
this.groupBox3.Controls.Add(this.propertyGrid1);
this.groupBox3.Controls.Add(this.groupBox1);
this.groupBox3.Controls.Add(this.chkDetectManualCharge);
this.groupBox3.Controls.Add(this.chkAGVDirBack);
this.groupBox3.Controls.Add(this.chkFullScreen);
this.groupBox3.Controls.Add(this.nudMusicVol);
this.groupBox3.Controls.Add(this.label22);
@@ -2690,7 +2765,7 @@
this.propertyGrid1.Font = new System.Drawing.Font("맑은 고딕", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.propertyGrid1.Location = new System.Drawing.Point(678, 109);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.Size = new System.Drawing.Size(538, 448);
this.propertyGrid1.Size = new System.Drawing.Size(538, 426);
this.propertyGrid1.TabIndex = 11;
//
// groupBox1
@@ -2731,18 +2806,6 @@
this.chkDetectManualCharge.Text = "수동 충전 감지";
this.chkDetectManualCharge.UseVisualStyleBackColor = true;
//
// chkAGVDirBack
//
this.chkAGVDirBack.AutoSize = true;
this.chkAGVDirBack.Font = new System.Drawing.Font("궁서체", 20F, System.Drawing.FontStyle.Bold);
this.chkAGVDirBack.ForeColor = System.Drawing.Color.Red;
this.chkAGVDirBack.Location = new System.Drawing.Point(27, 72);
this.chkAGVDirBack.Name = "chkAGVDirBack";
this.chkAGVDirBack.Size = new System.Drawing.Size(619, 31);
this.chkAGVDirBack.TabIndex = 8;
this.chkAGVDirBack.Text = "FVI 이동시 Backward 방향으로 이동 합니다";
this.chkAGVDirBack.UseVisualStyleBackColor = true;
//
// chkFullScreen
//
this.chkFullScreen.AutoSize = true;
@@ -3299,13 +3362,13 @@
this.btSave.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.btSave.Cursor = System.Windows.Forms.Cursors.Hand;
this.btSave.Dock = System.Windows.Forms.DockStyle.Bottom;
this.btSave.Font = new System.Drawing.Font("궁서체", 21.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btSave.Font = new System.Drawing.Font("궁서체", 30F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btSave.ForeColor = System.Drawing.Color.White;
this.btSave.GradientEnable = true;
this.btSave.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btSave.GradientRepeatBG = false;
this.btSave.isButton = true;
this.btSave.Location = new System.Drawing.Point(5, 740);
this.btSave.Location = new System.Drawing.Point(5, 720);
this.btSave.MouseDownColor = System.Drawing.Color.Yellow;
this.btSave.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btSave.msg = null;
@@ -3325,7 +3388,7 @@
this.btSave.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btSave.SignColor = System.Drawing.Color.Yellow;
this.btSave.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btSave.Size = new System.Drawing.Size(1270, 55);
this.btSave.Size = new System.Drawing.Size(1270, 75);
this.btSave.TabIndex = 8;
this.btSave.Text = "저장";
this.btSave.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -3333,41 +3396,6 @@
this.btSave.TextVisible = true;
this.btSave.Click += new System.EventHandler(this.btSave_Click);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label7.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label7.Location = new System.Drawing.Point(162, 524);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(101, 24);
this.label7.TabIndex = 79;
this.label7.Text = "Xbee ID";
this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// vcXBID
//
this.vcXBID.BackColorButton = System.Drawing.Color.White;
this.vcXBID.ButtonWidth = "30";
this.vcXBID.ColorBorder = System.Drawing.Color.White;
this.vcXBID.DecimalPosition = ((ushort)(0));
this.vcXBID.Font = new System.Drawing.Font("맑은 고딕", 26.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.vcXBID.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.vcXBID.ForeColor = System.Drawing.Color.White;
this.vcXBID.ForeColorButton = System.Drawing.Color.Black;
this.vcXBID.Location = new System.Drawing.Point(269, 509);
this.vcXBID.MaxValue = 9999D;
this.vcXBID.MinValue = 0D;
this.vcXBID.Name = "vcXBID";
this.vcXBID.NullDisplay = "--";
this.vcXBID.SideButtonClickValue = 1D;
this.vcXBID.Size = new System.Drawing.Size(164, 54);
this.vcXBID.TabIndex = 78;
this.vcXBID.Tag = "SDH";
this.vcXBID.Text = "30";
this.vcXBID.Value = 30D;
this.vcXBID.ButtonClick += new System.EventHandler<AGVControl.ValueSelect.ButtonClickEventArgs>(this.vcChargeLow_ButtonClick);
//
// fSetup
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
@@ -3529,7 +3557,6 @@
private AGVControl.ValueSelect vcChargeStartLevel;
private AGVControl.ValueSelect vcChargeMaxLevel;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.CheckBox chkAGVDirBack;
private System.Windows.Forms.CheckBox chkFullScreen;
private System.Windows.Forms.NumericUpDown nudMusicVol;
private System.Windows.Forms.Label label22;
@@ -3588,5 +3615,7 @@
private System.Windows.Forms.CheckBox chkClearPos;
private System.Windows.Forms.Label label7;
private AGVControl.ValueSelect vcXBID;
private AGVControl.ValueSelect vcGDS;
private System.Windows.Forms.Button button8;
}
}

View File

@@ -77,6 +77,7 @@ namespace Project
vcSTT.Value = PUB.setting.STT;
vcSAD.Value = PUB.setting.SAD;
vcXBID.Value = PUB.setting.XBE_ID;
vcGDS.Value = PUB.setting.GDSValue;
tbagvaddr.Text = PUB.setting.AGV_ADDRESS;
tbagvpanid.Text = PUB.setting.AGV_PANID;
@@ -115,10 +116,7 @@ namespace Project
SetColorValue(btSpeaker, PUB.setting.Enable_Speak);
SetColorValue(btAutoCharge, PUB.setting.Enable_AutoCharge);
SetColorValue(btMusic, PUB.setting.Enable_Music);
nudDoorSoundTerm.Value = PUB.setting.doorSoundTerm;
chkAGVDirBack.Checked = PUB.setting.AGV_Direction_FVI_Backward;
nudDoorSoundTerm.Value = PUB.setting.alarmSoundTerm;
chkClear1.Checked = PUB.setting.datetime_Check_1;
chkClear2.Checked = PUB.setting.datetime_Check_2;
cleartime1.Value = DateTime.Parse("1982-11-23 " + PUB.setting.datetime_Reset_1 + ":00");
@@ -262,6 +260,7 @@ namespace Project
PUB.setting.STT = (int)vcSTT.Value;
PUB.setting.SAD = (int)vcSAD.Value;
PUB.setting.XBE_ID = (byte)vcXBID.Value;
PUB.setting.GDSValue = (UInt16)vcGDS.Value;
//속도 저/중/고
PUB.setting.SPD_L = (int)vcSpeedL.Value;
@@ -297,7 +296,6 @@ namespace Project
PUB.setting.ChargeMaxTime = (int)vcChargeMaxTime.Value;
//Pub.setting.ChargeIdleInterval = (int)nudChargeIdleInterval.Value;
PUB.setting.ChargeRetryTerm = (int)vcChargeRetryTerm.Value;
PUB.setting.AGV_Direction_FVI_Backward = chkAGVDirBack.Checked;
//초기화시간
PUB.setting.datetime_Check_1 = chkClear1.Checked;
@@ -338,8 +336,8 @@ namespace Project
PUB.SetVolume((int)nudMusicVol.Value);
PUB.setting.ChargeSearchTime = (int)vcChargeSearchTime.Value;
PUB.setting.doorSoundTerm = (int)nudDoorSoundTerm.Value;
if (PUB.setting.doorSoundTerm < 1) PUB.setting.doorSoundTerm = 15; //기본값 15초
PUB.setting.alarmSoundTerm = (int)nudDoorSoundTerm.Value;
if (PUB.setting.alarmSoundTerm < 1) PUB.setting.alarmSoundTerm = 15; //기본값 15초
////핀설정
//var baL = new System.Collections.BitArray(new byte[] { PUB.setting.PinReverse_DI_Low });
@@ -673,5 +671,13 @@ namespace Project
if (rlt.Item2.isEmpty()) return;
tb.Text = rlt.Item2;
}
private void button8_Click(object sender, EventArgs e)
{
var value = (ushort)vcGDS.Value;
var rlt = PUB.AGV.TurnGDSCenterScope(value);
if (rlt == false) UTIL.MsgE("Error");
else UTIL.MsgI("OK");
}
}
}

View File

@@ -1,4 +1,74 @@
# CLAUDE.md
# ENIG AGV Control System Documentation
## 개요
이 문서는 ENIG AGV (Automated Guided Vehicle) 제어 소프트웨어의 동작 및 기능에 대한 기술 문서입니다. 본 시스템은 C# 기반의 상태 머신(State Machine) 아키텍처를 사용하여 AGV의 주행, 충전, 자재 이송 등의 작업을 제어합니다.
> **참고**: 현재 기능 개발이 진행 중이며, 일부 시퀀스는 변경될 수 있습니다.
## 핵심 기능 (Core Functionality)
### 1. 대기 및 준비 (IDLE / READY)
- **상태 파일**: `_SM_RUN_READY.cs`
- **기능**:
- AGV가 작업을 수행하지 않을 때의 기본 상태입니다.
- **자동 충전 모니터링**: 배터리 레벨을 지속적으로 확인하여 설정된 임계값 이하로 떨어지거나, 유휴 시간이 길어지면 자동으로 충전 시퀀스로 전환합니다.
- **충전 완료 체크**: 충전 중일 경우 최대 충전 시간 또는 목표 전압에 도달하면 충전을 중단하고 대기 상태로 복귀합니다.
- **정차 유지**: 대기 모드에서 AGV가 움직이지 않도록 지속적으로 정지 명령을 수행합니다.
### 2. 목적지 이동 (GOTO)
- **상태 파일**: `_SM_RUN_GOTO.cs`
- **기능**:
- 지정된 RFID 태그(노드)로 이동하는 주행 로직입니다.
- **경로 예측 (Prediction)**: 현재 위치에서 목적지까지의 경로를 계산하고, 필요한 회전(Turn)이나 분기 동작을 결정합니다.
- **속도 제어**: 직선 구간, 코너, 정지 전 감속 등 상황에 맞춰 속도(High, Mid, Low)를 조절합니다.
- **장애물 감지**: 주행 중 전방 장애물이 감지되면 즉시 정지하거나 감속합니다.
### 3. 충전 시퀀스 (CHARGE)
- **상태 파일**: `_SM_RUN_GOCHARGE.cs`, `_SM_RUN_GOCHARGECHECK.cs`
- **기능**:
- **충전기 이동**: 충전 스테이션의 RFID 위치를 찾아 이동합니다.
- **정밀 도킹**: 충전기 앞에서 정밀하게 위치를 조정하여 도킹합니다.
- **충전 시작/종료**: BMS(Battery Management System)와 통신하여 충전을 시작하고 상태를 모니터링합니다.
## 특수 시퀀스 (Special Sequences)
버퍼(Buffer) 스테이션에서의 자재 로딩/언로딩을 위한 특수 동작 시퀀스입니다.
### 1. 버퍼 진입 (BUFFER IN)
- **상태 파일**: `_SM_RUN_BUFFER_IN.cs`
- **동작 순서**:
1. **진입 전 확인**: 하드웨어 상태 및 위치 확인.
2. **회전 (Turn)**: 버퍼 진입을 위해 필요한 각도로 회전 (주로 180도 턴).
3. **리프트 다운 (Lift Down)**: 자재 적재를 위해 리프트를 하강 (코드상 확인 필요).
4. **후진 진입**: 저속으로 후진하여 버퍼 스테이션에 진입.
5. **마크 감지 정지**: 정지 마크를 감지하여 정확한 위치에 정차.
### 2. 버퍼 이탈 (BUFFER OUT)
- **상태 파일**: `_SM_RUN_BUFFER_OUT.cs`
- **동작 순서**:
1. **전진 이동**: 저속으로 전진하여 버퍼 스테이션을 빠져나옵니다.
2. **마크 스톱**: 이탈 완료 지점의 마크를 감지하여 정지합니다.
3. **회전 (Turn)**: 주행 방향으로 복귀하기 위해 우측 180도 회전을 수행합니다.
### 3. 동기화 (SYNC)
- **상태 파일**: `_SM_RUN_SYNC.cs`
- **기능**:
- 프로그램 시작 시 또는 연결 복구 시 AGV 하드웨어와 소프트웨어 간의 설정을 동기화합니다.
- 속도 PID 제어값, 주행 파라미터 등을 장비로 전송하고 확인(ACK) 받습니다.
- **UI 표시**: 동기화 진행률을 화면에 표시하여 사용자가 상태를 알 수 있게 합니다.
## 안전 기능 (Safety Features)
- **장애물 감지**: LiDAR 또는 센서를 통해 전방 물체 감지 시 음성 안내("전방에 물체가 감지되었습니다")와 함께 정지.
- **통신 타임아웃**: 하드웨어(AGV, BMS, XBee)와의 통신이 끊기면 에러 상태로 전환하거나 재연결을 시도.
- **비상 정지**: 소프트웨어적 비상 정지 및 하드웨어 E-Stop 상태 감지.
## 개발 상태
- 현재 기본 주행 및 충전 기능은 구현되어 있으나, 버퍼 시퀀스 및 예외 처리에 대한 고도화 작업이 진행 중입니다.
- 맵 에디터(`UnifiedAGVCanvas`)와 연동하여 실시간 모니터링 및 시뮬레이션 기능을 강화하고 있습니다.
---
# 기존 CLAUDE.md 내용 (참고용)
이 파일은 이 저장소의 코드로 작업할 때 Claude Code (claude.ai/code)를 위한 지침을 제공합니다.
맵데이터는 C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.agvmap 파일을 기준으로 사용
@@ -343,4 +413,4 @@ AGVNavigationCore/
### 🚨 알려진 이슈
- **빌드 환경**: MSBuild 2022가 설치되지 않은 환경에서 빌드 불가
- **좌표 시스템**: 줌/팬 상태에서 좌표 변환 정확성 지속 모니터링 필요
- **좌표 시스템**: 줌/팬 상태에서 좌표 변환 정확성 지속 모니터링 필요

View File

@@ -45,17 +45,13 @@ namespace Project.StateMachine
/// </summary>
public enum ERunStep : byte
{
/// <summary>
/// 자동모드 대기상태
/// </summary>
READY = 0,
/// <summary>
/// 홈(QC)로 이동합니다
/// </summary>
GOHOME,
/// <summary>
/// 충전을 해제 함
/// </summary>
@@ -66,7 +62,6 @@ namespace Project.StateMachine
/// </summary>
GOCHARGE,
/// <summary>
/// 충전중
/// </summary>
@@ -75,15 +70,20 @@ namespace Project.StateMachine
/// <summary>
/// 상차이동
/// </summary>
GOUP,
BUFFER_OUT,
/// <summary>
/// 하차이동
/// </summary>
GODOWN,
BUFFER_IN,
LOADER_IN,
LOADER_OUT,
UNLOADER_IN,
UNLOADER_OUT,
CLEANER_IN,
CLEANER_OUT,
/// <summary>
/// 목적지로 이동합니다
@@ -98,8 +98,12 @@ namespace Project.StateMachine
/// <summary>
/// 후진방향으로 마크스탑진행
/// </summary>
MARKSTROPB,
MARKSTOPB,
/// <summary>
/// 에러발생
/// </summary>
ERROR,
}

View File

@@ -283,6 +283,8 @@ namespace Project.StateMachine
private int _runStepSeq = 0;
public int RunStepSeq { get { return _runStepSeq; } }
public Stack<ERunStep> BackupRunStep = new Stack<ERunStep>();
private DateTime runStepStartTime = DateTime.Parse("1982-11-23");
private ERunStep _runstepn = ERunStep.READY;
private ERunStep _runstepo = ERunStep.READY;

View File

@@ -65,7 +65,7 @@ namespace arDev
{
return AddCommand(cmd + data);
}
public bool LiftControl(LiftCommand cmd)
{
return AddCommand(eAgvCmd.LiftControl, cmd.ToString());
@@ -86,9 +86,10 @@ namespace arDev
return AddCommand(eAgvCmd.GateoutTime, time.ToString("0000"));
}
public bool TurnGDSCenterScope()
public bool TurnGDSCenterScope(UInt16 time)
{
return AddCommand(eAgvCmd.TurnGDSCenterScope);
if (time > 2000) time = 2000;
return AddCommand(eAgvCmd.TurnGDSCenterScope, time.ToString("0000"));
}
public bool AGVMoveLeft180Turn()
{
@@ -98,9 +99,10 @@ namespace arDev
{
return AddCommand(eAgvCmd.TurnRight);
}
public bool AGVMoveBack180Turn()
public bool AGVMoveBack180Turn(bool leftTurn)
{
return AddCommand(eAgvCmd.TurnBack);
var dir = leftTurn ? "L" : "R";
return AddCommand(eAgvCmd.BackAndTurn, dir);
}
@@ -141,7 +143,7 @@ namespace arDev
$"SSK{interval:0000}",
$"SCK{value:0000}"
};
return AddCommand(cmds);
return AddCommand( cmds);
}
public bool AGVSetTagReinputTime(int value)
@@ -162,19 +164,29 @@ namespace arDev
$"{cmd}I{cmd2}{I:0000}",
$"{cmd}D{cmd2}{D:0000}",
};
return AddCommand(cmds);
return AddCommand( cmds);
}
/// <summary>
/// 전송을시도한 시간
/// </summary>
//public Dictionary<String,DateTime> LastCommandTime { get; set; }
/// <summary>
/// 전송에 성공한 명령시간
/// </summary>
//public Dictionary<String, DateTime> LastCommandOKTime { get; set; }
protected bool AddCommand(params string[] cmds)
{
bool ret = true;
ACKData = string.Empty; //회신값 제거
//LastCommandTime.Add(cmd, DateTime.Now);
foreach (var cmdline in cmds)
{
var fullcmd = MakeCheckSum(cmdline);
//commandQueue.Enqueue(fullcmd);
if (WriteData(fullcmd) == false) ret = false;
//else LastCommandOKTime.Add(cmd, DateTime.Now);
System.Threading.Thread.Sleep(1);
}
return ret;
@@ -240,8 +252,9 @@ namespace arDev
cmdString = $"CTR0000";
retval = AddCommand(cmdString);
break;
case eAgvCmd.TurnBack:
cmdString = $"CTB0000";
case eAgvCmd.BackAndTurn:
if (param.isEmpty()) param = "L";
cmdString = $"CTB000{param}";
retval = AddCommand(cmdString);
break;
case eAgvCmd.CallCancle:
@@ -270,7 +283,8 @@ namespace arDev
retval = AddCommand(cmdString);
break;
case eAgvCmd.TurnGDSCenterScope:
cmdString = "SGS1000";
if (param.isEmpty()) param = "1000";
cmdString = $"SGS{param}";
retval = AddCommand(cmdString);
break;
case eAgvCmd.BackTrunResumeTime:

View File

@@ -15,7 +15,7 @@ namespace arDev
public enum eMoveSpd
{
High,
Middle,
Mid,
Low,
}
public enum eSetPIDSpeed
@@ -73,12 +73,12 @@ namespace arDev
else if (bunki == 'R') Bunki = eBunki.Right;
if (speed == 'H') this.Speed = eMoveSpd.High;
else if (speed == 'M') this.Speed = eMoveSpd.Middle;
else if (speed == 'M') this.Speed = eMoveSpd.Mid;
else if (speed == 'L') this.Speed = eMoveSpd.Low;
}
public eMoveDir Direction { get; set; } = eMoveDir.Forward;
public eBunki Bunki { get; set; } = eBunki.Strate;
public eMoveSpd Speed { get; set; } = eMoveSpd.Middle;
public eMoveSpd Speed { get; set; } = eMoveSpd.Mid;
public int PBSSensor { get; set; } = 1;
public override string ToString()
{
@@ -151,7 +151,7 @@ namespace arDev
/// <summary>
/// CMD : CTB0000
/// </summary>
TurnBack,
BackAndTurn,
/// <summary>
/// CMD : CBZ

View File

@@ -12,7 +12,7 @@ namespace COMM
LPickOnCount,
SumQty,
ChargeWaitSec,
SyncItemCount,
}
public enum eVarUInt32
{
@@ -68,7 +68,7 @@ namespace COMM
/// <summary>
/// AGV 진행방향(UP = backward, DOWN = forward)
/// </summary>
AGVDIR_UP,
AGVDIR_BACK,
/// <summary>
/// 마크센서가 감지되면 활성화됨
@@ -112,10 +112,7 @@ namespace COMM
/// 충전시작명령을 전송했다
/// </summary>
WAIT_CHARGEACK,
MARKSTOP_ON,
//agv area start ( 64 ~ 95)
@@ -177,6 +174,7 @@ namespace COMM
/// </summary>
ReadyStart,
MarkSensorOn,
MarkSensorOff,
/// <summary>
@@ -194,8 +192,25 @@ namespace COMM
CheckGotoTargetSet,
SendGotoCommand,
/// <summary>
/// 마지막으로 멈춤 명령을 전송한 시간
/// </summary>
LastStopCommandTime,
/// <summary>
/// 마지막으로 멈춤(마크스탑) 명령을 전송한 시간
/// </summary>
LastMarkStopCommandTime,
/// <summary>
/// 마지막으로 턴 명령을 전송한 시간
/// </summary>
LastTurnCommandTime,
/// <summary>
/// 마지막을 실행 명령을 전송한 시간
/// </summary>
LastRunCommandTime,
}
}
}

Submodule Cs_HMI/SubProject/CommUtil updated: 632b087c5b...ed05439991

Submodule Cs_HMI/SubProject/EnigProtocol updated: 4f360f33a7...82bca1c90b

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Supertonic
{
class Program
{
class Args
{
public bool UseGpu { get; set; } = false;
public string OnnxDir { get; set; } = "assets/onnx";
public string BaseDir { get; set; } = "";
public int TotalStep { get; set; } = 5;
public float Speed { get; set; } = 1.05f;
public int NTest { get; set; } = 4;
public List<string> VoiceStyle { get; set; } = new List<string> { "assets/voice_styles/M2.json" };
public List<string> Text { get; set; } = new List<string>
{
"hello , 안녕하세요"
};
public string SaveDir { get; set; } = "results";
public bool Batch { get; set; } = false;
}
static Args ParseArgs(string[] args)
{
var result = new Args();
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "--use-gpu":
result.UseGpu = true;
break;
case "--batch":
result.Batch = true;
break;
case "--onnx-dir" when i + 1 < args.Length:
result.OnnxDir = args[++i];
break;
case "--total-step" when i + 1 < args.Length:
result.TotalStep = int.Parse(args[++i]);
break;
case "--speed" when i + 1 < args.Length:
result.Speed = float.Parse(args[++i]);
break;
case "--n-test" when i + 1 < args.Length:
result.NTest = int.Parse(args[++i]);
break;
case "--voice-style" when i + 1 < args.Length:
result.VoiceStyle = args[++i].Split(',').ToList();
break;
case "--text" when i + 1 < args.Length:
result.Text = args[++i].Split('|').ToList();
break;
case "--save-dir" when i + 1 < args.Length:
result.SaveDir = args[++i];
break;
case "--base-dir" when i + 1 < args.Length:
result.BaseDir = args[++i];
break;
}
}
return result;
}
static async Task Main(string[] args)
{
Console.WriteLine("=== TTS Inference with ONNX Runtime (C#) ===\n");
// --- 1. Parse arguments --- //
var parsedArgs = ParseArgs(args);
int totalStep = parsedArgs.TotalStep;
float speed = parsedArgs.Speed;
int nTest = parsedArgs.NTest;
string saveDir = parsedArgs.SaveDir;
var voiceStylePaths = parsedArgs.VoiceStyle;
var textList = parsedArgs.Text;
bool batch = parsedArgs.Batch;
if (voiceStylePaths.Count != textList.Count)
{
throw new ArgumentException(
$"Number of voice styles ({voiceStylePaths.Count}) must match number of texts ({textList.Count})");
}
int bsz = voiceStylePaths.Count;
// --- 2. Load Text to Speech --- //
var onnxdir = System.IO.Path.Combine(parsedArgs.BaseDir, parsedArgs.OnnxDir);
var textToSpeech = Helper.LoadTextToSpeech(onnxdir, parsedArgs.UseGpu);
Console.WriteLine();
// --- 3. Load Voice Style --- //
for(int i = 0; i < voiceStylePaths.Count;i++)
{
voiceStylePaths[i] = Path.Combine(parsedArgs.BaseDir, voiceStylePaths[i]);
}
var style = Helper.LoadVoiceStyle(voiceStylePaths, verbose: true);
// --- 4. Synthesize speech --- //
for (int n = 0; n < nTest; n++)
{
Console.WriteLine($"\n[{n + 1}/{nTest}] Starting synthesis...");
var (wav, duration) = Helper.Timer("Generating speech from text", () =>
{
if (batch)
{
return textToSpeech.Batch(textList, style, totalStep, speed);
}
else
{
return textToSpeech.Call(textList[0], style, totalStep, speed);
}
});
if (!Directory.Exists(saveDir))
{
Directory.CreateDirectory(saveDir);
}
var playbackTasks = new List<Task>();
for (int b = 0; b < bsz; b++)
{
string fname = $"{Helper.SanitizeFilename(textList[b], 20)}_{n + 1}.wav";
int wavLen = (int)(textToSpeech.SampleRate * duration[b]);
var wavOut = new float[wavLen];
Array.Copy(wav, b * wav.Length / bsz, wavOut, 0, Math.Min(wavLen, wav.Length / bsz));
string outputPath = Path.Combine(saveDir, fname);
Helper.WriteWavFile(outputPath, wavOut, textToSpeech.SampleRate);
Console.WriteLine($"Saved: {outputPath}");
// 비동기로 오디오 재생 (파일 저장과 동시에)
Console.WriteLine($"Playing audio [{b + 1}/{bsz}]...");
var playTask = Helper.PlayAudioAsync(wavOut, textToSpeech.SampleRate);
playbackTasks.Add(playTask);
}
// 모든 재생이 완료될 때까지 대기
await Task.WhenAll(playbackTasks);
Console.WriteLine("Playback completed.");
}
Console.WriteLine("\n=== Synthesis completed successfully! ===");
}
}
}

View File

@@ -0,0 +1,944 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Media;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
namespace Supertonic
{
// ============================================================================
// Configuration classes
// ============================================================================
public class Config
{
public AEConfig AE { get; set; } = null;
public TTLConfig TTL { get; set; } = null;
public class AEConfig
{
public int SampleRate { get; set; }
public int BaseChunkSize { get; set; }
}
public class TTLConfig
{
public int ChunkCompressFactor { get; set; }
public int LatentDim { get; set; }
}
}
// ============================================================================
// Style class
// ============================================================================
public class Style
{
public float[] Ttl { get; set; }
public long[] TtlShape { get; set; }
public float[] Dp { get; set; }
public long[] DpShape { get; set; }
public Style(float[] ttl, long[] ttlShape, float[] dp, long[] dpShape)
{
Ttl = ttl;
TtlShape = ttlShape;
Dp = dp;
DpShape = dpShape;
}
}
// ============================================================================
// Unicode text processor
// ============================================================================
public class UnicodeProcessor
{
private readonly Dictionary<int, long> _indexer;
public UnicodeProcessor(string unicodeIndexerPath)
{
var json = File.ReadAllText(unicodeIndexerPath);
var indexerArray = JsonSerializer.Deserialize<long[]>(json) ?? throw new Exception("Failed to load indexer");
_indexer = new Dictionary<int, long>();
for (int i = 0; i < indexerArray.Length; i++)
{
_indexer[i] = indexerArray[i];
}
}
private static string RemoveEmojis(string text)
{
var result = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
int codePoint;
if (char.IsHighSurrogate(text[i]) && i + 1 < text.Length && char.IsLowSurrogate(text[i + 1]))
{
// Get the full code point from surrogate pair
codePoint = char.ConvertToUtf32(text[i], text[i + 1]);
i++; // Skip the low surrogate
}
else
{
codePoint = text[i];
}
// Check if code point is in emoji ranges
bool isEmoji = (codePoint >= 0x1F600 && codePoint <= 0x1F64F) ||
(codePoint >= 0x1F300 && codePoint <= 0x1F5FF) ||
(codePoint >= 0x1F680 && codePoint <= 0x1F6FF) ||
(codePoint >= 0x1F700 && codePoint <= 0x1F77F) ||
(codePoint >= 0x1F780 && codePoint <= 0x1F7FF) ||
(codePoint >= 0x1F800 && codePoint <= 0x1F8FF) ||
(codePoint >= 0x1F900 && codePoint <= 0x1F9FF) ||
(codePoint >= 0x1FA00 && codePoint <= 0x1FA6F) ||
(codePoint >= 0x1FA70 && codePoint <= 0x1FAFF) ||
(codePoint >= 0x2600 && codePoint <= 0x26FF) ||
(codePoint >= 0x2700 && codePoint <= 0x27BF) ||
(codePoint >= 0x1F1E6 && codePoint <= 0x1F1FF);
if (!isEmoji)
{
if (codePoint > 0xFFFF)
{
// Add back as surrogate pair
result.Append(char.ConvertFromUtf32(codePoint));
}
else
{
result.Append((char)codePoint);
}
}
}
return result.ToString();
}
private string PreprocessText(string text)
{
// TODO: Need advanced normalizer for better performance
text = text.Normalize(NormalizationForm.FormKD);
// FIXME: this should be fixed for non-English languages
// Remove emojis (wide Unicode range)
// C# doesn't support \u{...} syntax in regex, so we use character filtering instead
text = RemoveEmojis(text);
// Replace various dashes and symbols
var replacements = new Dictionary<string, string>
{
{"", "-"}, // en dash
{"", "-"}, // non-breaking hyphen
{"—", "-"}, // em dash
{"¯", " "}, // macron
{"_", " "}, // underscore
{"\u201C", "\""}, // left double quote
{"\u201D", "\""}, // right double quote
{"\u2018", "'"}, // left single quote
{"\u2019", "'"}, // right single quote
{"´", "'"}, // acute accent
{"`", "'"}, // grave accent
{"[", " "}, // left bracket
{"]", " "}, // right bracket
{"|", " "}, // vertical bar
{"/", " "}, // slash
{"#", " "}, // hash
{"→", " "}, // right arrow
{"←", " "}, // left arrow
};
foreach (var kvp in replacements)
{
text = text.Replace(kvp.Key, kvp.Value);
}
// Remove combining diacritics // FIXME: this should be fixed for non-English languages
text = Regex.Replace(text, @"[\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u030A\u030B\u030C\u0327\u0328\u0329\u032A\u032B\u032C\u032D\u032E\u032F]", "");
// Remove special symbols
text = Regex.Replace(text, @"[♥☆♡©\\]", "");
// Replace known expressions
var exprReplacements = new Dictionary<string, string>
{
{"@", " at "},
{"e.g.,", "for example, "},
{"i.e.,", "that is, "},
};
foreach (var kvp in exprReplacements)
{
text = text.Replace(kvp.Key, kvp.Value);
}
// Fix spacing around punctuation
text = Regex.Replace(text, @" ,", ",");
text = Regex.Replace(text, @" \.", ".");
text = Regex.Replace(text, @" !", "!");
text = Regex.Replace(text, @" \?", "?");
text = Regex.Replace(text, @" ;", ";");
text = Regex.Replace(text, @" :", ":");
text = Regex.Replace(text, @" '", "'");
// Remove duplicate quotes
while (text.Contains("\"\""))
{
text = text.Replace("\"\"", "\"");
}
while (text.Contains("''"))
{
text = text.Replace("''", "'");
}
while (text.Contains("``"))
{
text = text.Replace("``", "`");
}
// Remove extra spaces
text = Regex.Replace(text, @"\s+", " ").Trim();
// If text doesn't end with punctuation, quotes, or closing brackets, add a period
if (!Regex.IsMatch(text, @"[.!?;:,'\u0022\u201C\u201D\u2018\u2019)\]}…。」』】〉》›»]$"))
{
text += ".";
}
return text;
}
private int[] TextToUnicodeValues(string text)
{
return text.Select(c => (int)c).ToArray();
}
private float[][][] GetTextMask(long[] textIdsLengths)
{
return Helper.LengthToMask(textIdsLengths);
}
public (long[][] textIds, float[][][] textMask) Call(List<string> textList)
{
var processedTexts = textList.Select(t => PreprocessText(t)).ToList();
var textIdsLengths = processedTexts.Select(t => (long)t.Length).ToArray();
long maxLen = textIdsLengths.Max();
var textIds = new long[textList.Count][];
for (int i = 0; i < processedTexts.Count; i++)
{
textIds[i] = new long[maxLen];
var unicodeVals = TextToUnicodeValues(processedTexts[i]);
for (int j = 0; j < unicodeVals.Length; j++)
{
if (_indexer.TryGetValue(unicodeVals[j], out long val))
{
textIds[i][j] = val;
}
}
}
var textMask = GetTextMask(textIdsLengths);
return (textIds, textMask);
}
}
// ============================================================================
// TextToSpeech class
// ============================================================================
public class TextToSpeech
{
private readonly Config _cfgs;
private readonly UnicodeProcessor _textProcessor;
private readonly InferenceSession _dpOrt;
private readonly InferenceSession _textEncOrt;
private readonly InferenceSession _vectorEstOrt;
private readonly InferenceSession _vocoderOrt;
public readonly int SampleRate;
private readonly int _baseChunkSize;
private readonly int _chunkCompressFactor;
private readonly int _ldim;
public TextToSpeech(
Config cfgs,
UnicodeProcessor textProcessor,
InferenceSession dpOrt,
InferenceSession textEncOrt,
InferenceSession vectorEstOrt,
InferenceSession vocoderOrt)
{
_cfgs = cfgs;
_textProcessor = textProcessor;
_dpOrt = dpOrt;
_textEncOrt = textEncOrt;
_vectorEstOrt = vectorEstOrt;
_vocoderOrt = vocoderOrt;
SampleRate = cfgs.AE.SampleRate;
_baseChunkSize = cfgs.AE.BaseChunkSize;
_chunkCompressFactor = cfgs.TTL.ChunkCompressFactor;
_ldim = cfgs.TTL.LatentDim;
}
private (float[][][] noisyLatent, float[][][] latentMask) SampleNoisyLatent(float[] duration)
{
int bsz = duration.Length;
float wavLenMax = duration.Max() * SampleRate;
var wavLengths = duration.Select(d => (long)(d * SampleRate)).ToArray();
int chunkSize = _baseChunkSize * _chunkCompressFactor;
int latentLen = (int)((wavLenMax + chunkSize - 1) / chunkSize);
int latentDim = _ldim * _chunkCompressFactor;
// Generate random noise
var random = new Random();
var noisyLatent = new float[bsz][][];
for (int b = 0; b < bsz; b++)
{
noisyLatent[b] = new float[latentDim][];
for (int d = 0; d < latentDim; d++)
{
noisyLatent[b][d] = new float[latentLen];
for (int t = 0; t < latentLen; t++)
{
// Box-Muller transform for normal distribution
double u1 = 1.0 - random.NextDouble();
double u2 = 1.0 - random.NextDouble();
noisyLatent[b][d][t] = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Cos(2.0 * Math.PI * u2));
}
}
}
var latentMask = Helper.GetLatentMask(wavLengths, _baseChunkSize, _chunkCompressFactor);
// Apply mask
for (int b = 0; b < bsz; b++)
{
for (int d = 0; d < latentDim; d++)
{
for (int t = 0; t < latentLen; t++)
{
noisyLatent[b][d][t] *= latentMask[b][0][t];
}
}
}
return (noisyLatent, latentMask);
}
private (float[] wav, float[] duration) _Infer(List<string> textList, Style style, int totalStep, float speed = 1.05f)
{
int bsz = textList.Count;
if (bsz != style.TtlShape[0])
{
throw new ArgumentException("Number of texts must match number of style vectors");
}
// Process text
var (textIds, textMask) = _textProcessor.Call(textList);
var textIdsShape = new long[] { bsz, textIds[0].Length };
var textMaskShape = new long[] { bsz, 1, textMask[0][0].Length };
var textIdsTensor = Helper.IntArrayToTensor(textIds, textIdsShape);
var textMaskTensor = Helper.ArrayToTensor(textMask, textMaskShape);
var styleTtlTensor = new DenseTensor<float>(style.Ttl, style.TtlShape.Select(x => (int)x).ToArray());
var styleDpTensor = new DenseTensor<float>(style.Dp, style.DpShape.Select(x => (int)x).ToArray());
// Run duration predictor
var dpInputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("text_ids", textIdsTensor),
NamedOnnxValue.CreateFromTensor("style_dp", styleDpTensor),
NamedOnnxValue.CreateFromTensor("text_mask", textMaskTensor)
};
using (var dpOutputs = _dpOrt.Run(dpInputs))
{
var durOnnx = dpOutputs.First(o => o.Name == "duration").AsTensor<float>().ToArray();
// Apply speed factor to duration
for (int i = 0; i < durOnnx.Length; i++)
{
durOnnx[i] /= speed;
}
// Run text encoder
var textEncInputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("text_ids", textIdsTensor),
NamedOnnxValue.CreateFromTensor("style_ttl", styleTtlTensor),
NamedOnnxValue.CreateFromTensor("text_mask", textMaskTensor)
};
using (var textEncOutputs = _textEncOrt.Run(textEncInputs))
{
var textEmbTensor = textEncOutputs.First(o => o.Name == "text_emb").AsTensor<float>();
// Sample noisy latent
var (xt, latentMask) = SampleNoisyLatent(durOnnx);
var latentShape = new long[] { bsz, xt[0].Length, xt[0][0].Length };
var latentMaskShape = new long[] { bsz, 1, latentMask[0][0].Length };
var totalStepArray = Enumerable.Repeat((float)totalStep, bsz).ToArray();
// Iterative denoising
for (int step = 0; step < totalStep; step++)
{
var currentStepArray = Enumerable.Repeat((float)step, bsz).ToArray();
var vectorEstInputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("noisy_latent", Helper.ArrayToTensor(xt, latentShape)),
NamedOnnxValue.CreateFromTensor("text_emb", textEmbTensor),
NamedOnnxValue.CreateFromTensor("style_ttl", styleTtlTensor),
NamedOnnxValue.CreateFromTensor("text_mask", textMaskTensor),
NamedOnnxValue.CreateFromTensor("latent_mask", Helper.ArrayToTensor(latentMask, latentMaskShape)),
NamedOnnxValue.CreateFromTensor("total_step", new DenseTensor<float>(totalStepArray, new int[] { bsz })),
NamedOnnxValue.CreateFromTensor("current_step", new DenseTensor<float>(currentStepArray, new int[] { bsz }))
};
using (var vectorEstOutputs = _vectorEstOrt.Run(vectorEstInputs))
{
var denoisedLatent = vectorEstOutputs.First(o => o.Name == "denoised_latent").AsTensor<float>();
// Update xt
int idx = 0;
for (int b = 0; b < bsz; b++)
{
for (int d = 0; d < xt[b].Length; d++)
{
for (int t = 0; t < xt[b][d].Length; t++)
{
xt[b][d][t] = denoisedLatent.GetValue(idx++);
}
}
}
}
}
// Run vocoder
var vocoderInputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("latent", Helper.ArrayToTensor(xt, latentShape))
};
using (var vocoderOutputs = _vocoderOrt.Run(vocoderInputs))
{
var wavTensor = vocoderOutputs.First(o => o.Name == "wav_tts").AsTensor<float>();
return (wavTensor.ToArray(), durOnnx);
}
}
}
}
public (float[] wav, float[] duration) Call(string text, Style style, int totalStep, float speed = 1.05f, float silenceDuration = 0.3f)
{
if (style.TtlShape[0] != 1)
{
throw new ArgumentException("Single speaker text to speech only supports single style");
}
var textList = Helper.ChunkText(text);
var wavCat = new List<float>();
float durCat = 0.0f;
foreach (var chunk in textList)
{
var (wav, duration) = _Infer(new List<string> { chunk }, style, totalStep, speed);
if (wavCat.Count == 0)
{
wavCat.AddRange(wav);
durCat = duration[0];
}
else
{
int silenceLen = (int)(silenceDuration * SampleRate);
var silence = new float[silenceLen];
wavCat.AddRange(silence);
wavCat.AddRange(wav);
durCat += duration[0] + silenceDuration;
}
}
return (wavCat.ToArray(), new float[] { durCat });
}
public (float[] wav, float[] duration) Batch(List<string> textList, Style style, int totalStep, float speed = 1.05f)
{
return _Infer(textList, style, totalStep, speed);
}
}
// ============================================================================
// Helper class with utility functions
// ============================================================================
public static class Helper
{
// ============================================================================
// Utility functions
// ============================================================================
public static float[][][] LengthToMask(long[] lengths, long maxLen = -1)
{
if (maxLen == -1)
{
maxLen = lengths.Max();
}
var mask = new float[lengths.Length][][];
for (int i = 0; i < lengths.Length; i++)
{
mask[i] = new float[1][];
mask[i][0] = new float[maxLen];
for (int j = 0; j < maxLen; j++)
{
mask[i][0][j] = j < lengths[i] ? 1.0f : 0.0f;
}
}
return mask;
}
public static float[][][] GetLatentMask(long[] wavLengths, int baseChunkSize, int chunkCompressFactor)
{
int latentSize = baseChunkSize * chunkCompressFactor;
var latentLengths = wavLengths.Select(len => (len + latentSize - 1) / latentSize).ToArray();
return LengthToMask(latentLengths);
}
// ============================================================================
// ONNX model loading
// ============================================================================
public static InferenceSession LoadOnnx(string onnxPath, SessionOptions opts)
{
return new InferenceSession(onnxPath, opts);
}
public static (InferenceSession dp, InferenceSession textEnc, InferenceSession vectorEst, InferenceSession vocoder)
LoadOnnxAll(string onnxDir, SessionOptions opts)
{
var dpPath = Path.Combine(onnxDir, "duration_predictor.onnx");
var textEncPath = Path.Combine(onnxDir, "text_encoder.onnx");
var vectorEstPath = Path.Combine(onnxDir, "vector_estimator.onnx");
var vocoderPath = Path.Combine(onnxDir, "vocoder.onnx");
return (
LoadOnnx(dpPath, opts),
LoadOnnx(textEncPath, opts),
LoadOnnx(vectorEstPath, opts),
LoadOnnx(vocoderPath, opts)
);
}
// ============================================================================
// Configuration loading
// ============================================================================
public static Config LoadCfgs(string onnxDir)
{
var cfgPath = Path.Combine(onnxDir, "tts.json");
var json = File.ReadAllText(cfgPath);
using (var doc = JsonDocument.Parse(json))
{
var root = doc.RootElement;
return new Config
{
AE = new Config.AEConfig
{
SampleRate = root.GetProperty("ae").GetProperty("sample_rate").GetInt32(),
BaseChunkSize = root.GetProperty("ae").GetProperty("base_chunk_size").GetInt32()
},
TTL = new Config.TTLConfig
{
ChunkCompressFactor = root.GetProperty("ttl").GetProperty("chunk_compress_factor").GetInt32(),
LatentDim = root.GetProperty("ttl").GetProperty("latent_dim").GetInt32()
}
};
}
}
public static UnicodeProcessor LoadTextProcessor(string onnxDir)
{
var unicodeIndexerPath = Path.Combine(onnxDir, "unicode_indexer.json");
return new UnicodeProcessor(unicodeIndexerPath);
}
// ============================================================================
// Voice style loading
// ============================================================================
public static Style LoadVoiceStyle(List<string> voiceStylePaths, bool verbose = false)
{
int bsz = voiceStylePaths.Count;
// Read first file to get dimensions
var firstJson = File.ReadAllText(voiceStylePaths[0]);
using (var firstDoc = JsonDocument.Parse(firstJson))
{
var firstRoot = firstDoc.RootElement;
var ttlDims = ParseInt64Array(firstRoot.GetProperty("style_ttl").GetProperty("dims"));
var dpDims = ParseInt64Array(firstRoot.GetProperty("style_dp").GetProperty("dims"));
long ttlDim1 = ttlDims[1];
long ttlDim2 = ttlDims[2];
long dpDim1 = dpDims[1];
long dpDim2 = dpDims[2];
// Pre-allocate arrays with full batch size
int ttlSize = (int)(bsz * ttlDim1 * ttlDim2);
int dpSize = (int)(bsz * dpDim1 * dpDim2);
var ttlFlat = new float[ttlSize];
var dpFlat = new float[dpSize];
// Fill in the data
for (int i = 0; i < bsz; i++)
{
var json = File.ReadAllText(voiceStylePaths[i]);
using (var doc = JsonDocument.Parse(json))
{
var root = doc.RootElement;
// Flatten data
var ttlData3D = ParseFloat3DArray(root.GetProperty("style_ttl").GetProperty("data"));
var ttlDataFlat = new List<float>();
foreach (var batch in ttlData3D)
{
foreach (var row in batch)
{
ttlDataFlat.AddRange(row);
}
}
var dpData3D = ParseFloat3DArray(root.GetProperty("style_dp").GetProperty("data"));
var dpDataFlat = new List<float>();
foreach (var batch in dpData3D)
{
foreach (var row in batch)
{
dpDataFlat.AddRange(row);
}
}
// Copy to pre-allocated array
int ttlOffset = (int)(i * ttlDim1 * ttlDim2);
ttlDataFlat.CopyTo(ttlFlat, ttlOffset);
int dpOffset = (int)(i * dpDim1 * dpDim2);
dpDataFlat.CopyTo(dpFlat, dpOffset);
}
}
var ttlShape = new long[] { bsz, ttlDim1, ttlDim2 };
var dpShape = new long[] { bsz, dpDim1, dpDim2 };
if (verbose)
{
Console.WriteLine($"Loaded {bsz} voice styles");
}
return new Style(ttlFlat, ttlShape, dpFlat, dpShape);
}
}
private static float[][][] ParseFloat3DArray(JsonElement element)
{
var result = new List<float[][]>();
foreach (var batch in element.EnumerateArray())
{
var batch2D = new List<float[]>();
foreach (var row in batch.EnumerateArray())
{
var rowData = new List<float>();
foreach (var val in row.EnumerateArray())
{
rowData.Add(val.GetSingle());
}
batch2D.Add(rowData.ToArray());
}
result.Add(batch2D.ToArray());
}
return result.ToArray();
}
private static long[] ParseInt64Array(JsonElement element)
{
var result = new List<long>();
foreach (var val in element.EnumerateArray())
{
result.Add(val.GetInt64());
}
return result.ToArray();
}
// ============================================================================
// TextToSpeech loading
// ============================================================================
public static TextToSpeech LoadTextToSpeech(string onnxDir, bool useGpu = false)
{
var opts = new SessionOptions();
if (useGpu)
{
throw new NotImplementedException("GPU mode is not supported yet");
}
else
{
Console.WriteLine("Using CPU for inference");
}
var cfgs = LoadCfgs(onnxDir);
var (dpOrt, textEncOrt, vectorEstOrt, vocoderOrt) = LoadOnnxAll(onnxDir, opts);
var textProcessor = LoadTextProcessor(onnxDir);
return new TextToSpeech(cfgs, textProcessor, dpOrt, textEncOrt, vectorEstOrt, vocoderOrt);
}
// ============================================================================
// WAV file writing
// ============================================================================
public static void WriteWavFile(string filename, float[] audioData, int sampleRate)
{
using (var writer = new BinaryWriter(File.Open(filename, FileMode.Create)))
{
int numChannels = 1;
int bitsPerSample = 16;
int byteRate = sampleRate * numChannels * bitsPerSample / 8;
short blockAlign = (short)(numChannels * bitsPerSample / 8);
int dataSize = audioData.Length * bitsPerSample / 8;
// RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16); // fmt chunk size
writer.Write((short)1); // audio format (PCM)
writer.Write((short)numChannels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write(blockAlign);
writer.Write((short)bitsPerSample);
// data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
// Write audio data
foreach (var sample in audioData)
{
float clamped = Math.Max(-1.0f, Math.Min(1.0f, sample));
short intSample = (short)(clamped * 32767);
writer.Write(intSample);
}
}
}
// ============================================================================
// Tensor conversion utilities
// ============================================================================
public static DenseTensor<float> ArrayToTensor(float[][][] array, long[] dims)
{
var flat = new List<float>();
foreach (var batch in array)
{
foreach (var row in batch)
{
flat.AddRange(row);
}
}
return new DenseTensor<float>(flat.ToArray(), dims.Select(x => (int)x).ToArray());
}
public static DenseTensor<long> IntArrayToTensor(long[][] array, long[] dims)
{
var flat = new List<long>();
foreach (var row in array)
{
flat.AddRange(row);
}
return new DenseTensor<long>(flat.ToArray(), dims.Select(x => (int)x).ToArray());
}
// ============================================================================
// Timer utility
// ============================================================================
public static T Timer<T>(string name, Func<T> func)
{
var start = DateTime.Now;
Console.WriteLine($"{name}...");
var result = func();
var elapsed = (DateTime.Now - start).TotalSeconds;
Console.WriteLine($" -> {name} completed in {elapsed:F2} sec");
return result;
}
public static string SanitizeFilename(string text, int maxLen)
{
var result = new StringBuilder();
int count = 0;
foreach (char c in text)
{
if (count >= maxLen) break;
if (char.IsLetterOrDigit(c))
{
result.Append(c);
}
else
{
result.Append('_');
}
count++;
}
return result.ToString();
}
// ============================================================================
// Chunk text
// ============================================================================
public static List<string> ChunkText(string text, int maxLen = 300)
{
var chunks = new List<string>();
// Split by paragraph (two or more newlines)
var paragraphRegex = new Regex(@"\n\s*\n+");
var paragraphs = paragraphRegex.Split(text.Trim())
.Select(p => p.Trim())
.Where(p => !string.IsNullOrEmpty(p))
.ToList();
// Split by sentence boundaries, excluding abbreviations
var sentenceRegex = new Regex(@"(?<!Mr\.|Mrs\.|Ms\.|Dr\.|Prof\.|Sr\.|Jr\.|Ph\.D\.|etc\.|e\.g\.|i\.e\.|vs\.|Inc\.|Ltd\.|Co\.|Corp\.|St\.|Ave\.|Blvd\.)(?<!\b[A-Z]\.)(?<=[.!?])\s+");
foreach (var paragraph in paragraphs)
{
var sentences = sentenceRegex.Split(paragraph);
string currentChunk = "";
foreach (var sentence in sentences)
{
if (string.IsNullOrEmpty(sentence)) continue;
if (currentChunk.Length + sentence.Length + 1 <= maxLen)
{
if (!string.IsNullOrEmpty(currentChunk))
{
currentChunk += " ";
}
currentChunk += sentence;
}
else
{
if (!string.IsNullOrEmpty(currentChunk))
{
chunks.Add(currentChunk.Trim());
}
currentChunk = sentence;
}
}
if (!string.IsNullOrEmpty(currentChunk))
{
chunks.Add(currentChunk.Trim());
}
}
// If no chunks were created, return the original text
if (chunks.Count == 0)
{
chunks.Add(text.Trim());
}
return chunks;
}
// ============================================================================
// Audio Playback
// ============================================================================
/// <summary>
/// 오디오를 스피커로 비동기 재생합니다.
/// </summary>
/// <param name="audioData">float 배열 형태의 오디오 데이터 (-1.0 ~ 1.0)</param>
/// <param name="sampleRate">샘플레이트 (예: 16000, 22050, 44100)</param>
public static async Task PlayAudioAsync(float[] audioData, int sampleRate)
{
await Task.Run(() =>
{
try
{
// 임시 WAV 파일 생성
string tempWavFile = Path.Combine(Path.GetTempPath(), $"temp_audio_{Guid.NewGuid()}.wav");
WriteWavFile(tempWavFile, audioData, sampleRate);
// SoundPlayer로 재생
using (var player = new SoundPlayer(tempWavFile))
{
player.PlaySync(); // 재생이 끝날 때까지 대기
}
// 임시 파일 삭제
try
{
File.Delete(tempWavFile);
}
catch { /* 임시 파일 삭제 실패 무시 */ }
}
catch (Exception ex)
{
Console.WriteLine($"[Audio Playback Error] {ex.Message}");
}
});
}
/// <summary>
/// 오디오 파일을 스피커로 비동기 재생합니다.
/// </summary>
/// <param name="wavFilePath">WAV 파일 경로</param>
public static async Task PlayWavFileAsync(string wavFilePath)
{
await Task.Run(() =>
{
try
{
using (var player = new SoundPlayer(wavFilePath))
{
player.PlaySync(); // 재생이 끝날 때까지 대기
}
}
catch (Exception ex)
{
Console.WriteLine($"[WAV Playback Error] {ex.Message}");
}
});
}
}
}

View File

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

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project=".\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.props" Condition="Exists('.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.props')" />
<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>{19675E19-EB91-493E-88C3-32B3C094B749}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Supertonic</RootNamespace>
<AssemblyName>Supertonic.Net48</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Supertonic.Program</StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\Microsoft.Bcl.AsyncInterfaces.10.0.1\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.OnnxRuntime, Version=1.23.2.0, Culture=neutral, PublicKeyToken=f27f157f0a5b7bb6, processorArchitecture=MSIL">
<HintPath>.\packages\Microsoft.ML.OnnxRuntime.Managed.1.23.2\lib\netstandard2.0\Microsoft.ML.OnnxRuntime.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.IO.Pipelines, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.IO.Pipelines.10.0.1\lib\net462\System.IO.Pipelines.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>.\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>.\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encodings.Web, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.Text.Encodings.Web.10.0.1\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference>
<Reference Include="System.Text.Json, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.Text.Json.10.0.1\lib\net462\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>.\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ExampleONNX.cs" />
<Compile Include="Helper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project=".\packages\Microsoft.ML.OnnxRuntime.Managed.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.Managed.targets" Condition="Exists('.\packages\Microsoft.ML.OnnxRuntime.Managed.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.Managed.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('.\packages\Microsoft.ML.OnnxRuntime.Managed.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.Managed.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Microsoft.ML.OnnxRuntime.Managed.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.Managed.targets'))" />
<Error Condition="!Exists('.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.props')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.props'))" />
<Error Condition="!Exists('.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.targets'))" />
<Error Condition="!Exists('.\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
</Target>
<Import Project=".\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.targets" Condition="Exists('.\packages\Microsoft.ML.OnnxRuntime.1.23.2\build\netstandard2.0\Microsoft.ML.OnnxRuntime.targets')" />
<Import Project=".\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('.\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
</Project>

View File

@@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 15 for Windows Desktop
VisualStudioVersion = 15.0.36324.19
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Supertonic.Netfx48", "Supertonic.Netfx48.csproj", "{19675E19-EB91-493E-88C3-32B3C094B749}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|ARM.ActiveCfg = Debug|ARM
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|ARM.Build.0 = Debug|ARM
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|ARM64.ActiveCfg = Debug|ARM64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|ARM64.Build.0 = Debug|ARM64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.ActiveCfg = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.Build.0 = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.ActiveCfg = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.Build.0 = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.Build.0 = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|ARM.ActiveCfg = Release|ARM
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|ARM.Build.0 = Release|ARM
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|ARM64.ActiveCfg = Release|ARM64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|ARM64.Build.0 = Release|ARM64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.ActiveCfg = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.Build.0 = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.ActiveCfg = Release|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F2E20C5-E704-4B99-8FE9-54394113916E}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Some files were not shown because too many files have changed in this diff Show More