diff --git a/Tnb.Server.sln b/Tnb.Server.sln
index f9236fb7..ed93b136 100644
--- a/Tnb.Server.sln
+++ b/Tnb.Server.sln
@@ -163,15 +163,12 @@ Global
{9FA1FB84-71AB-42A7-9570-F856A4B1EBAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FA1FB84-71AB-42A7-9570-F856A4B1EBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FA1FB84-71AB-42A7-9570-F856A4B1EBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9FA1FB84-71AB-42A7-9570-F856A4B1EBAB}.Release|Any CPU.Build.0 = Release|Any CPU
{135D0C0A-9B95-45F2-BE5F-01286F6BB234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{135D0C0A-9B95-45F2-BE5F-01286F6BB234}.Debug|Any CPU.Build.0 = Debug|Any CPU
{135D0C0A-9B95-45F2-BE5F-01286F6BB234}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {135D0C0A-9B95-45F2-BE5F-01286F6BB234}.Release|Any CPU.Build.0 = Release|Any CPU
{D1135D42-7CD0-4579-82B3-D2B121FCE954}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1135D42-7CD0-4579-82B3-D2B121FCE954}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1135D42-7CD0-4579-82B3-D2B121FCE954}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D1135D42-7CD0-4579-82B3-D2B121FCE954}.Release|Any CPU.Build.0 = Release|Any CPU
{8D3E0381-4B3D-4F44-81C8-535E28418A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D3E0381-4B3D-4F44-81C8-535E28418A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D3E0381-4B3D-4F44-81C8-535E28418A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -283,15 +280,12 @@ Global
{EE11B516-1B20-44E0-8691-A532B6F7B2EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE11B516-1B20-44E0-8691-A532B6F7B2EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE11B516-1B20-44E0-8691-A532B6F7B2EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EE11B516-1B20-44E0-8691-A532B6F7B2EC}.Release|Any CPU.Build.0 = Release|Any CPU
{461075DC-9C3A-45BB-97CC-939A2EBCEA4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{461075DC-9C3A-45BB-97CC-939A2EBCEA4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{461075DC-9C3A-45BB-97CC-939A2EBCEA4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {461075DC-9C3A-45BB-97CC-939A2EBCEA4E}.Release|Any CPU.Build.0 = Release|Any CPU
{3CCF286D-4C49-49E3-8AB1-2B1216E0ACFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CCF286D-4C49-49E3-8AB1-2B1216E0ACFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CCF286D-4C49-49E3-8AB1-2B1216E0ACFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3CCF286D-4C49-49E3-8AB1-2B1216E0ACFA}.Release|Any CPU.Build.0 = Release|Any CPU
{CA896F39-32F4-44C9-B512-A21A0363EB95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA896F39-32F4-44C9-B512-A21A0363EB95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA896F39-32F4-44C9-B512-A21A0363EB95}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Configs/ElevatorControlConfiguration.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Configs/ElevatorControlConfiguration.cs
new file mode 100644
index 00000000..e9cc09d3
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Configs/ElevatorControlConfiguration.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.WarehouseMgr.Entities.Configs
+{
+ public class ElevatorControlConfiguration
+ {
+ ///
+ /// 设备名称
+ ///
+ public string DevName { get; set; }
+ public string token { get; set; }
+ ///
+ /// 获取设备标签列表url
+ ///
+ public string GetTagListUrl { get; set; }
+ ///
+ /// 获取设备单个标签url
+ ///
+ public string GetTagUrl { get; set; }
+ ///
+ /// 写入设备标签属性url
+ ///
+ public string WriteTagUrl { get; set; }
+
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs b/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs
new file mode 100644
index 00000000..bfef5c8d
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr/AgvHeartbeatMonitorService.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using JNPF;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Tnb.Common.Extension;
+using Tnb.Common.Utils;
+using Tnb.WarehouseMgr.Entities.Configs;
+
+namespace Tnb.WarehouseMgr
+{
+ ///
+ /// Agv心跳检测服务
+ ///
+ public class AgvHeartbeatMonitorService : BackgroundService
+ {
+ private readonly ElevatorControlConfiguration _elevatorControlConfiguration;
+ public bool IsStarted { get; set; }
+
+ public AgvHeartbeatMonitorService()
+ {
+ _elevatorControlConfiguration = App.Configuration.Build();
+ }
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ IsStarted = true;
+ var parameter = new Dictionary();
+ parameter["DevName"] = _elevatorControlConfiguration.DevName;
+ parameter["TagName"] = "AGVKeepalive";
+ parameter["Value"] = "123";
+ parameter["token"] = _elevatorControlConfiguration.token;
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ await HttpClientHelper.GetAsync(_elevatorControlConfiguration.WriteTagUrl, parameter);
+ await Task.Delay(TimeSpan.FromMinutes(1));
+ }
+ }
+
+ public override Task StopAsync(CancellationToken cancellationToken)
+ {
+ IsStarted = false;
+ return Task.CompletedTask;
+ }
+ }
+
+ public static class AgvHeartbeatMonitorServiceExtenstions
+ {
+ public static IServiceCollection AddAgvHeartbeatMonitor(this IServiceCollection services)
+ {
+ return services.AddSingleton();
+ }
+ }
+
+
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs b/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs
new file mode 100644
index 00000000..22692589
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr/ElevatorControlService.cs
@@ -0,0 +1,328 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+using JNPF;
+using JNPF.DependencyInjection;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using MimeKit.Cryptography;
+using Newtonsoft.Json.Linq;
+using Tnb.Common.Extension;
+using Tnb.Common.Utils;
+using Tnb.WarehouseMgr.Entities.Configs;
+
+namespace Tnb.WarehouseMgr
+{
+ ///
+ /// 电梯控制业务服务类
+ ///
+ public class ElevatorControlService : BaseWareHouseService
+ {
+ private readonly ElevatorControlConfiguration _elevatorCtlCfg = new();
+ private readonly BackgroundService _agvHeartbeatMonitor;
+ private static Dictionary> _fetchStartedStausValue = new();
+ private bool isFrontDoorBit = false; //是否到前门位
+
+ public ElevatorControlService(BackgroundService agvHeartbeatMonitorService)
+ {
+ _elevatorCtlCfg = App.Configuration.Build();
+ _agvHeartbeatMonitor = agvHeartbeatMonitorService;
+ }
+
+ ///
+ /// 一楼电梯操作流程
+ ///
+ ///
+ [HttpPost]
+ public async Task FirstFloorElevatorFlow(string value)
+ {
+ CancellationTokenSource cts = new CancellationTokenSource();//扩展用
+ //获取电梯状态
+ (int sysStatus, int runStatus, int floorNo) multi = (-1, -1, -1);
+ do
+ {
+ multi = await GetElevatorStatus(CancellationToken.None);
+ await Task.Delay(2000);
+ } while (multi.sysStatus != 3 && multi.runStatus != 0);
+ if (multi.sysStatus == 3 && multi.runStatus == 0 && multi.floorNo != 1)
+ {
+ //如果不是1楼,判断电梯状态,如果电梯是空闲的向电梯发送一个到一楼的指令
+ var wirteRes = await SetAgvControlStatus(0);
+ JObject jo = JObject.Parse(wirteRes);
+
+ if (true) //此处为Agv到一楼开门位,通知操作目前默认为true
+ {
+ var propName = "IsStarted";
+ if (!_fetchStartedStausValue.TryGetValue(propName, out var func))
+ {
+ var isStartedProp = _agvHeartbeatMonitor.GetType().GetProperty(propName);
+ var paramExp = Expression.Parameter(typeof(BackgroundService), "_agvHeartbeatMonitor");
+ var propExp = Expression.Property(Expression.ConvertChecked(paramExp, isStartedProp.DeclaringType), isStartedProp);
+ var body = Expression.Lambda>(propExp, paramExp);
+ func = body.Compile();
+ _fetchStartedStausValue[propName] = func;
+ }
+ var isStarted = func(_agvHeartbeatMonitor);
+ if (!isStarted)
+ {
+ _agvHeartbeatMonitor.StartAsync(cts.Token);
+ }
+ //向电梯发送前门开门指令
+ await SendOpenCloseCmd(3);
+ //获取门状态 是否为 开门到位保持
+ var doorStatus = await GetTagAsync("DoorStatus");
+ if (doorStatus == 3)
+ {
+ //通知Agv进入电梯,放货,默认为true
+ if (true)
+ {
+ //向电梯发送关门指令
+ await SendOpenCloseCmd(4);
+ }
+ }
+
+ }
+ }
+ return Task.FromResult(0);
+ }
+ ///
+ /// 三楼电梯操作流程
+ ///
+ ///
+ [HttpPost]
+ public async Task ThreeFloorElevatorFlow()
+ {
+ //监听电梯门是否为关闭的状态
+ var dataStatus = 0;
+ do
+ {
+ dataStatus = await GetTagAsync("DoorStatus");
+ } while (dataStatus != 4);
+ if (dataStatus == 4)
+ {
+ //根据状态确认关闭后,向电梯发送指令从1~3楼 //FloorExecute 楼层触发
+ await WriteTagAsync("FloorExecute", 48);
+ //获取电梯到达3楼的到位信号
+ (_, int runStatus, int floorNo) = (-1, -1, -1);
+ do
+ {
+ var multi = await GetElevatorStatus(CancellationToken.None);
+ runStatus = multi.sysStatus; floorNo = multi.floorNo;
+ } while (runStatus != 0 && floorNo != 3);
+ // 控制电梯到达指定楼层默认开门的行为,改为默认不开门 条件:Agv 到达3楼开门位时,为true
+ if (true)//3楼Agv到达开门位后,向电梯发送开门指令, 默认true 当前
+ {
+
+
+ //向电梯发送前门开门指令
+ await SendOpenCloseCmd(3);
+ //获取门状态 是否为 开门到位保持
+ var doorStatus = await GetTagAsync("DoorStatus");
+ if (doorStatus == 3) //开门到位保持状态
+ {
+ //通知Agv进入电梯,取货
+ //向电梯发送关门指令
+ await SendOpenCloseCmd(4);
+ doorStatus = await GetTagAsync("DoorStatus");
+ if (doorStatus == 4) //门状态,为关门到位保持
+ {
+ //解锁,Agv状态,将其至为
+ var agvStatus = await GetTagAsync("AGVStatus");
+ if (agvStatus != 2)
+ {
+ await SetAgvControlStatus(0);
+ }
+ }
+ }
+ }
+ }
+ return Task.FromResult(0);
+ }
+
+ ///
+ /// x2server测试
+ ///
+ ///
+ [HttpGet]
+ public async Task X2ServerTest()
+ {
+ //var r = new Random();
+ int currentValue = 0;
+
+ while (true)
+ {
+ var readRes = await GetElevatorStatus(CancellationToken.None);
+ await Console.Out.WriteLineAsync($"接收结果:{readRes}");
+
+ currentValue = 1 - currentValue; // 切换为0或1
+ dynamic obj = new ExpandoObject();
+ obj.TagName = "AGVControl";
+ obj.Value = currentValue;
+ var parameter = await SetParameter(obj);
+ var wirteRes = await HttpClientHelper.GetAsync(_elevatorCtlCfg.WriteTagUrl, pars: parameter);
+ await Console.Out.WriteLineAsync($"写入结果:{wirteRes}");
+ await Task.Delay(1000);
+ }
+ }
+
+ private Task