Browse Source

[feat] 添加 JiLinApp.Docking, JiLinApp.Biz 库

master
fajiao 1 year ago
parent
commit
90d6e70b4c
  1. 2
      EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs
  2. 4
      EC.Util/CameraSDK/HiK/HiKSdk.cs
  3. 2
      EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs
  4. 6
      EC.Util/CameraSDK/YuShi/YuShiSdk.cs
  5. 17
      JiLinApp.Biz/JiLinApp.Biz.csproj
  6. 14
      JiLinApp.Biz/TransmitAlarm/Common/AlarmServiceFactory.cs
  7. 48
      JiLinApp.Biz/TransmitAlarm/Common/Config.cs
  8. 34
      JiLinApp.Biz/TransmitAlarm/Entity/AlarmMessage.cs
  9. 42
      JiLinApp.Biz/TransmitAlarm/Entity/AlarmMessageHelper.cs
  10. 332
      JiLinApp.Biz/TransmitAlarm/Service/AlarmMqttService.cs
  11. 52
      JiLinApp.Biz/TransmitAlarm/Service/AlarmZmqService.cs
  12. 44
      JiLinApp.Biz/TransmitAlarm/Service/Interfaces/IAlarmService.cs
  13. 9
      JiLinApp.Docking/Alarm/AlarmCode.cs
  14. 40
      JiLinApp.Docking/Alarm/AlarmCodeHelper.cs
  15. 25
      JiLinApp.Docking/FenceAlarm/Entity/DeviceStateMessage.cs
  16. 76
      JiLinApp.Docking/FenceAlarm/Entity/SectorState.cs
  17. 158
      JiLinApp.Docking/FenceAlarm/Entity/UdpAlarmHost.cs
  18. 29
      JiLinApp.Docking/FenceAlarm/Entity/UdpAlarmHostMessage.cs
  19. 31
      JiLinApp.Docking/FenceAlarm/Entity/UdpDatagramReceivedEventArgs.cs
  20. 10
      JiLinApp.Docking/FenceAlarm/Entity/UdpManagerConfig.cs
  21. 256
      JiLinApp.Docking/FenceAlarm/Service/AlarmEncode.cs
  22. 793
      JiLinApp.Docking/FenceAlarm/Service/UdpManager.cs
  23. 136
      JiLinApp.Docking/FenceAlarm/Service/UdpServer.cs
  24. 25
      JiLinApp.Docking/JiLinApp.Docking.csproj
  25. 16
      JiLinApp.Docking/Military/Config.cs
  26. 18
      JiLinApp.Docking/Military/Entity/CameraLinkageInfo.cs
  27. 165
      JiLinApp.Docking/Military/MilitaryService.cs
  28. 128
      JiLinApp.Docking/Ptz/DCamera.cs
  29. 240
      JiLinApp.Docking/Ptz/PelcoD.cs
  30. 337
      JiLinApp.Docking/Ptz/PelcoP.cs
  31. 415
      JiLinApp.Docking/Ptz/PtzCmd.cs
  32. 38
      JiLinApp.Docking/Ptz/PtzConfig.cs
  33. 130
      JiLinApp.Docking/VibrateAlarm/Entity/ClientMessage.cs
  34. 80
      JiLinApp.Docking/VibrateAlarm/Entity/DataMessage.cs
  35. 12
      JiLinApp.Docking/VibrateAlarm/Entity/DataRequest.cs
  36. 68
      JiLinApp.Docking/VibrateAlarm/Entity/SensorState.cs
  37. 22
      JiLinApp.Docking/VibrateAlarm/Entity/TcpAlarmHost.cs
  38. 19
      JiLinApp.Docking/VibrateAlarm/Entity/TcpAlarmHostMessage.cs
  39. 10
      JiLinApp.Docking/VibrateAlarm/Entity/TcpManagerConfig.cs
  40. 20
      JiLinApp.Docking/VibrateAlarm/Entity/TcpSensorTable.cs
  41. 436
      JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs
  42. 49
      JiLinApp.Docking/VibrateAlarm/Service/CRC8.cs
  43. 23
      JiLinApp.Docking/VibrateAlarm/Service/TcpClientConnectedEventArgs.cs
  44. 23
      JiLinApp.Docking/VibrateAlarm/Service/TcpClientDisconnectedEventArgs.cs
  45. 53
      JiLinApp.Docking/VibrateAlarm/Service/TcpClientState.cs
  46. 29
      JiLinApp.Docking/VibrateAlarm/Service/TcpDatagramReceivedEventArgs.cs
  47. 765
      JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs
  48. 2222
      JiLinApp.Docking/config/alarmcode.json
  49. 12
      JiLinCpApp.sln

2
EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs

@ -39,7 +39,7 @@ public static class DaHuaOriSdk
if (InitSuccess) return true; if (InitSuccess) return true;
bool ret = CLIENT_InitEx(null, IntPtr.Zero, IntPtr.Zero); bool ret = CLIENT_InitEx(null, IntPtr.Zero, IntPtr.Zero);
InitSuccess = ret; InitSuccess = ret;
if (!ret) throw new Exception("DaHuaOriSDK global init failure."); if (!ret) throw new Exception("DaHuaOriSdk global init failure.");
return ret; return ret;
} }

4
EC.Util/CameraSDK/HiK/HiKSdk.cs

@ -39,8 +39,8 @@ public class HiKSdk : ICameraSdk
ret = ConnectSuccess(); ret = ConnectSuccess();
if (ret) HiKOriSdk.NET_DVR_SetReconnect(10000, 1); if (ret) HiKOriSdk.NET_DVR_SetReconnect(10000, 1);
else BuildException(); else BuildException();
//HiKOriSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new(); //HiKOriSdk.NET_DVR_DEVICEINFO_V30 deviceInfo = new();
//LoginId = HiKOriSDK.NET_DVR_Login_V30(CameraInfo.Ip, CameraInfo.Port, CameraInfo.UserName, CameraInfo.Password, ref deviceInfo); //LoginId = HiKOriSdk.NET_DVR_Login_V30(CameraInfo.Ip, CameraInfo.Port, CameraInfo.UserName, CameraInfo.Password, ref deviceInfo);
return ret; return ret;
} }

2
EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs

@ -39,7 +39,7 @@ public class YuShiOriSdk
if (InitSuccess) return true; if (InitSuccess) return true;
bool ret = NETDEV_Init(); bool ret = NETDEV_Init();
InitSuccess = ret; InitSuccess = ret;
if (!ret) throw new Exception("YuShiOriSDK global init failure."); if (!ret) throw new Exception("YuShiOriSdk global init failure.");
return ret; return ret;
} }

6
EC.Util/CameraSDK/YuShi/YuShiSdk.cs

@ -111,9 +111,9 @@ public class YuShiSdk : ICameraSdk
{ {
hPlayWnd = hwnd, hPlayWnd = hwnd,
dwChannelID = Channel, dwChannelID = Channel,
dwStreamType = 0, // YuShiOriSDK.NETDEV_LIVE_STREAM_INDEX_E dwStreamType = 0, // YuShiOriSdk.NETDEV_LIVE_STREAM_INDEX_E
dwLinkMode = 1, // YuShiOriSDK.NETDEV_PROTOCAL_E dwLinkMode = 1, // YuShiOriSdk.NETDEV_PROTOCAL_E
dwFluency = 0, // YuShiOriSDK.NETDEV_PICTURE_FLUENCY_E dwFluency = 0, // YuShiOriSdk.NETDEV_PICTURE_FLUENCY_E
}; };
RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, IntPtr.Zero, IntPtr.Zero); RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, IntPtr.Zero, IntPtr.Zero);
if (RealplayHandle == IntPtr.Zero) BuildException(); if (RealplayHandle == IntPtr.Zero) BuildException();

17
JiLinApp.Biz/JiLinApp.Biz.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MQTTnet" Version="4.1.4.563" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JiLinApp.Docking\JiLinApp.Docking.csproj" />
</ItemGroup>
</Project>

14
JiLinApp.Biz/TransmitAlarm/Common/AlarmServiceFactory.cs

@ -0,0 +1,14 @@
namespace JiLinApp.Biz.TransmitAlarm;
public class AlarmServiceFactory
{
public static IAlarmService CreateService(AlarmPlatformConfig config)
{
IAlarmService service = (config.Type) switch
{
"mqtt" => new AlarmMqttService(config.Mqtt),
_ => throw new NotSupportedException(),
}; ;
return service;
}
}

48
JiLinApp.Biz/TransmitAlarm/Common/Config.cs

@ -0,0 +1,48 @@
namespace JiLinApp.Biz.TransmitAlarm;
public class AlarmPlatformConfig
{
public bool RealPlay { get; set; }
public string Type { get; set; }
public MqttConfig Mqtt { get; set; }
}
public class MqttConfig
{
//public bool Local { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string ClientId { get; set; }
public int RetryTime { get; set; }
public int RetryInterval { get; set; }
public string PubAlarmTopic { get; set; }
public string PubDevicesTopic { get; set; }
public string PubSensorsTopic { get; set; }
public string PubDeviceStateTopic { get; set; }
public string PubSensorStateTopic { get; set; }
public string SubCmdTopic { get; set; }
}
public class ZmqConfig
{
public string AlarmPubAddr { get; set; }
public string AlarmPubTopic { get; set; }
}

34
JiLinApp.Biz/TransmitAlarm/Entity/AlarmMessage.cs

@ -0,0 +1,34 @@
namespace JiLinApp.Biz.TransmitAlarm;
public class AlarmMessage
{
/** (必填)传感器设备编码*/
public string LabelCode { get; set; }
public string ChannelId { get; set; }
/** (必填)报警类型((1-视频报警,2-雷达报警;3-微振动警报,4-电子围网警报,9-其他报警))*/
public int WarnType { get; set; }
/** (必填)报警级别(1-5)*/
public int WarnLevel { get; set; }
/** (必填)报警内容*/
public string WarnContent { get; set; }
/** 处置方式*/
public int DealWay { get; set; }
/** (必填)处置状态(1-未处理,2-已处理)*/
public int DealStatus { get; set; }
/** (必填)发生地点*/
public string CameraLoc { get; set; }
/** (必填)发生时间*/
public DateTime CreateTime { get; set; }
/** 负责人*/
public string Director { get; set; }
/** (必填)类型(1-预警,2-报警,3-故障,)*/
public int Kind { get; set; }
/** 现场照片*/
public string ImgUrl { get; set; }
public override string ToString()
{
return string.Format("{{ labelCode:{0}, channelId:{1}, warnType:{2}, warnLevel:{3}, warnContent:{4}, createTime:{5} }}",
LabelCode, ChannelId, WarnType, WarnLevel, WarnContent, CreateTime);
}
}

42
JiLinApp.Biz/TransmitAlarm/Entity/AlarmMessageHelper.cs

@ -0,0 +1,42 @@
using JiLinApp.Docking.Alarm;
using JiLinApp.Docking.FenceAlarm;
using JiLinApp.Docking.VibrateAlarm;
namespace JiLinApp.Biz.TransmitAlarm;
public static class AlarmMessageHelper
{
public static AlarmMessage ToAlarmMessage(this TcpAlarmHostMessage msg)
{
AlarmCode code = AlarmCodeHelper.Get(msg.CID);
AlarmMessage obj = new()
{
LabelCode = Convert.ToString(msg.DeviceID),
ChannelId = msg.SensorAddr,
WarnType = 3,
WarnLevel = code.Level,
WarnContent = code.Content,
DealStatus = 1,
CreateTime = Convert.ToDateTime(msg.AlarmTime),
Kind = 2,
};
return obj;
}
public static AlarmMessage ToAlarmMessage(this UdpAlarmHostMessage msg)
{
AlarmCode code = AlarmCodeHelper.Get(msg.CID);
AlarmMessage obj = new()
{
LabelCode = Convert.ToString(msg.DeviceId),
ChannelId = Convert.ToString(msg.SectorId),
WarnType = 4,
WarnLevel = code.Level,
WarnContent = code.Content,
DealStatus = 1,
CreateTime = Convert.ToDateTime(msg.AlarmTime),
Kind = 2,
};
return obj;
}
}

332
JiLinApp.Biz/TransmitAlarm/Service/AlarmMqttService.cs

@ -0,0 +1,332 @@
using EC.Util.Common;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
using MQTTnet.Server;
using NewLife.Serialization;
using Newtonsoft.Json.Linq;
using System.Text;
namespace JiLinApp.Biz.TransmitAlarm;
public class AlarmMqttService : IAlarmService
{
#region Fields
private MqttConfig Config { get; }
//private MqttServer? Server { get; }
private IMqttClient Client { get; }
//private MqttServerOptions ServerOptions { get; }
private MqttClientOptions ClientOptions { get; }
private MqttClientSubscribeOptions SubscribeOptions { get; }
public event IAlarmService.HandleRecvEvent? OnFenceUdpSendDevices;
public event IAlarmService.HandleRecvEvent? OnVibrateTcpSendDevices;
public event IAlarmService.HandleRecvEvent? OnFenceUdpSendSensors;
public event IAlarmService.HandleRecvEvent? OnVibrateTcpSendSensors;
#endregion Fields
public AlarmMqttService(MqttConfig config)
{
MqttFactory factory = new();
ClientOptions = factory.CreateClientOptionsBuilder()
.WithTcpServer(config.Ip, config.Port)
.WithClientId(config.ClientId)
.WithCredentials(config.UserName, config.Password)
.WithCleanSession(false)
.WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
.Build();
SubscribeOptions = factory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(config.SubCmdTopic))
.Build();
//if (config.Local)
//{
// ServerOptions = factory.CreateServerOptionsBuilder()
// .WithDefaultEndpoint()
// .WithDefaultEndpointPort(config.Port)
// .Build();
// MqttServer server = factory.CreateMqttServer(ServerOptions);
// server.ValidatingConnectionAsync += Server_ValidatingConnectionAsync;
// server.ClientConnectedAsync += Server_ClientConnectedAsync;
// server.ClientDisconnectedAsync += Server_ClientDisconnectedAsync;
// server.ClientAcknowledgedPublishPacketAsync += Server_ClientAcknowledgedPublishPacketAsync;
// Server = server;
//}
IMqttClient client = factory.CreateMqttClient();
client.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync;
Config = config;
Client = client;
}
~AlarmMqttService()
{
Close();
}
#region Base
public void Start()
{
if (IsRuning()) return;
int retryTime = Config.RetryTime, retryInterval = Config.RetryInterval;
for (int i = 1; i <= retryTime; i++)
{
MqttClientConnectResult connResult = Client.ConnectAsync(ClientOptions, CancellationToken.None).Result;
if (connResult.ResultCode == MqttClientConnectResultCode.Success) break;
if (i == retryTime) throw new MqttCommunicationTimedOutException();
Thread.Sleep(retryInterval);
}
for (int i = 1; i <= retryTime; i++)
{
MqttClientSubscribeResult subResult = Client.SubscribeAsync(SubscribeOptions, CancellationToken.None).Result;
bool flag = true;
foreach (var item in subResult.Items)
{
if (item.ResultCode > MqttClientSubscribeResultCode.GrantedQoS2)
{
flag = false;
break;
}
}
if (flag) break;
if (i == retryTime) throw new MqttCommunicationTimedOutException();
Thread.Sleep(retryInterval);
}
}
public void Close()
{
if (!IsRuning()) return;
Client.DisconnectAsync().Wait();
}
public bool IsRuning()
{
return Client != null && Client.IsConnected;
}
private object StartAtomObj { get; } = new();
private bool StartAtom()
{
lock (StartAtomObj)
{
if (!IsRuning()) Start();
}
return IsRuning();
}
#endregion Base
#region Server Event
private Task Server_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg)
{
if (arg.ClientId.Length == 0) arg.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
else if (arg.UserName != Config.UserName) arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
else if (arg.Password != Config.Password) arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
return Task.CompletedTask;
}
private Task Server_ClientConnectedAsync(ClientConnectedEventArgs arg)
{
return Task.CompletedTask;
}
private Task Server_ClientDisconnectedAsync(ClientDisconnectedEventArgs arg)
{
return Task.CompletedTask;
}
private Task Server_ClientAcknowledgedPublishPacketAsync(ClientAcknowledgedPublishPacketEventArgs arg)
{
string clientId = arg.ClientId;
ushort packetIdentifier = arg.PublishPacket.PacketIdentifier;
string topic = arg.PublishPacket.Topic;
string msg = Encoding.UTF8.GetString(arg.PublishPacket.Payload);
return Task.CompletedTask;
}
#endregion Server Event
#region Client Event
private Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
string topic = arg.ApplicationMessage.Topic;
string msg = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
string qos = arg.ApplicationMessage.QualityOfServiceLevel.ToString();
string retained = arg.ApplicationMessage.Retain.ToString();
if (topic.Equals(Config.SubCmdTopic))
{
HandleRecvSubCmdTopic(topic, msg);
}
return Task.CompletedTask;
}
private void HandleRecvSubCmdTopic(string topic, string reqJson)
{
JObject reqObj = JsonUtil.ToJObject(reqJson);
string cmd = reqObj.GetValue("cmd", StringComparison.OrdinalIgnoreCase)?.ToString() ?? "";
DeviceType type = (DeviceType)(reqObj.GetValue("type", StringComparison.OrdinalIgnoreCase).ToInt());
switch (cmd.Trim().ToLower())
{
case "getdevices":
switch (type)
{
case DeviceType.Vibrate:
OnVibrateTcpSendDevices?.Invoke(reqObj);
break;
case DeviceType.Fence:
OnFenceUdpSendDevices?.Invoke(reqObj);
break;
}
break;
case "getsensors":
switch (type)
{
case DeviceType.Vibrate:
OnVibrateTcpSendSensors?.Invoke(reqObj);
break;
case DeviceType.Fence:
OnFenceUdpSendSensors?.Invoke(reqObj);
break;
}
break;
}
}
#endregion Client Event
#region Send
private int Frame { get; set; } = 0;
private int IncFrame
{ get { return ++Frame % int.MaxValue; } }
private void Send(string topic, object obj)
{
MqttApplicationMessage mqttMsg = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(obj.ToJson())
.Build();
MqttClientPublishResult? pubResult = null;
while (pubResult == null || !pubResult.IsSuccess)
{
try
{
pubResult = Client.PublishAsync(mqttMsg, CancellationToken.None).Result;
}
catch (Exception)
{
while (!IsRuning())
{
if (StartAtom()) break;
Thread.Sleep(100);
}
}
}
}
private void SendByFrame(string topic, object obj)
{
//((dynamic)obj).Frame = IncFrame;// no work, transto dict, add frame, transto json
MqttApplicationMessage mqttMsg = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(obj.ToJson())
.Build();
MqttClientPublishResult? pubResult = null;
while (pubResult == null || !pubResult.IsSuccess)
{
try
{
pubResult = Client.PublishAsync(mqttMsg, CancellationToken.None).Result;
}
catch (Exception)
{
while (!IsRuning())
{
if (StartAtom()) break;
Thread.Sleep(100);
}
}
}
}
public void SendAlarm(AlarmMessage msg)
{
Send(Config.PubAlarmTopic, msg);
}
public void SendDevices(DeviceType type, List<object> deviceList)
{
object obj = new
{
type = type,
data = deviceList,
frame = IncFrame
};
Send(Config.PubDevicesTopic, obj);
}
public void SendSensors(DeviceType type, int deviceId, List<object> sensorList)
{
object obj = new
{
type = type,
deviceId = deviceId,
data = sensorList,
frame = IncFrame
};
Send(Config.PubSensorsTopic, obj);
}
public void SendDeviceState(DeviceType type, object device)
{
object obj = new
{
type = type,
data = device,
frame = IncFrame
};
Send(Config.PubDeviceStateTopic, obj);
}
public void SendSensorState(DeviceType type, object sensor)
{
object obj = new
{
type = type,
data = sensor,
frame = IncFrame
};
Send(Config.PubSensorStateTopic, obj);
}
#endregion Send
}
public enum DeviceType : int
{
Unknown = 0,
Vibrate = 3,
Fence = 4,
}

52
JiLinApp.Biz/TransmitAlarm/Service/AlarmZmqService.cs

@ -0,0 +1,52 @@
namespace JiLinApp.Biz.TransmitAlarm;
public class AlarmZmqService : IAlarmService
{
public event IAlarmService.HandleRecvEvent? OnFenceUdpSendDevices;
public event IAlarmService.HandleRecvEvent? OnVibrateTcpSendDevices;
public event IAlarmService.HandleRecvEvent? OnFenceUdpSendSensors;
public event IAlarmService.HandleRecvEvent? OnVibrateTcpSendSensors;
public void Start()
{
throw new NotImplementedException();
}
public void Close()
{
throw new NotImplementedException();
}
public bool IsRuning()
{
throw new NotImplementedException();
}
public void SendAlarm(AlarmMessage msg)
{
throw new NotImplementedException();
}
public void SendDevices(DeviceType type, List<object> deviceList)
{
throw new NotImplementedException();
}
public void SendSensors(DeviceType type, int deviceId, List<object> sensorList)
{
throw new NotImplementedException();
}
public void SendDeviceState(DeviceType type, object device)
{
throw new NotImplementedException();
}
public void SendSensorState(DeviceType type, object sensor)
{
throw new NotImplementedException();
}
}

44
JiLinApp.Biz/TransmitAlarm/Service/Interfaces/IAlarmService.cs

@ -0,0 +1,44 @@
using Newtonsoft.Json.Linq;
namespace JiLinApp.Biz.TransmitAlarm;
public interface IAlarmService
{
#region Event
public delegate void HandleRecvEvent(JObject reqObj);
public event HandleRecvEvent? OnFenceUdpSendDevices;
public event HandleRecvEvent? OnVibrateTcpSendDevices;
public event HandleRecvEvent? OnFenceUdpSendSensors;
public event HandleRecvEvent? OnVibrateTcpSendSensors;
#endregion Event
#region Base
void Start();
void Close();
bool IsRuning();
#endregion Base
#region Send
void SendAlarm(AlarmMessage msg);
void SendDevices(DeviceType type, List<object> deviceList);
void SendSensors(DeviceType type, int deviceId, List<object> sensorList);
void SendDeviceState(DeviceType type, object device);
void SendSensorState(DeviceType type, object sensor);
#endregion Send
}

9
JiLinApp.Docking/Alarm/AlarmCode.cs

@ -0,0 +1,9 @@
namespace JiLinApp.Docking.Alarm;
public class AlarmCode
{
public string Id { get; set; }
public int Level { get; set; }
public string Type { get; set; }
public string Content { get; set; }
}

40
JiLinApp.Docking/Alarm/AlarmCodeHelper.cs

@ -0,0 +1,40 @@
using Newtonsoft.Json;
namespace JiLinApp.Docking.Alarm;
public class AlarmCodeHelper
{
#region Fields
private static readonly Dictionary<string, AlarmCode> AlarmCodeDict;
private static readonly List<int> LevelList;
private static readonly List<string> TypeList;
#endregion Fields
static AlarmCodeHelper()
{
using StreamReader r = new(Path.Combine("config", "alarmcode.json"));
string jsonStr = r.ReadToEnd();
List<AlarmCode> list = JsonConvert.DeserializeObject<List<AlarmCode>>(jsonStr) ?? new();
AlarmCodeDict = list.ToDictionary(item => item.Id, item => item);
LevelList = list.GroupBy(item => item.Level).Select(it => it.First().Level).ToList();
TypeList = list.GroupBy(item => item.Type).Select(it => it.First().Type).ToList();
}
public static void Init()
{
}
public static AlarmCode Get(string id)
{
return AlarmCodeDict[id];
}
public static bool TryGet(string id, out AlarmCode alarmCode)
{
bool flag = AlarmCodeDict.TryGetValue(id, out AlarmCode? temp);
alarmCode = temp ?? new();
return flag;
}
}

25
JiLinApp.Docking/FenceAlarm/Entity/DeviceStateMessage.cs

@ -0,0 +1,25 @@
namespace JiLinApp.Docking.FenceAlarm;
public class DeviceStateMessage
{
public int deviceID { get; set; }//设备唯一ID
public string StatusTime { get; set; }//时间
public int channel { get; set; }//防区
public int StatusType { get; set; }//状态
public DeviceStateMessage(int id, DateTime time, int Channel, int statusType)
{
deviceID = id;
StatusTime = time.ToString("yyyy-MM-dd HH:mm:ss");
channel = Channel;
StatusType = statusType;
}
public DeviceStateMessage()
{
deviceID = -1;
StatusTime = "";
channel = -1;
StatusType = -1;
}
}

76
JiLinApp.Docking/FenceAlarm/Entity/SectorState.cs

@ -0,0 +1,76 @@
namespace JiLinApp.Docking.FenceAlarm;
public class SectorState
{
#region Fields
public int DeviceId { get; set; }
public int Id { get; set; }//防区号
/// <summary>
/// 0: 防区未使用
/// 1: 防区撤防
/// 2: 防区布防
/// 3: 防区旁路
/// 4: 防区报警
/// 5: 无线防区欠压
/// 6: 防区掉线(与主线总线脱离)
/// </summary>
public int State { get; set; }//防区状态
public string StateStr
{
get
{
return State switch
{
0 => "防区未使用",
1 => "防区撤防",
2 => "防区布防",
3 => "防区旁路",
4 => "防区报警",
5 => "无线防区欠压",
6 => "防区掉线",
_ => "未知状态:" + State,
};
}
}
/// <summary>
/// 0: 普通防区,无特殊参数
/// 1: 张力防区需要单独查询因为这个防区显示张力线值每条线状态
/// 2: 脉冲围栏
/// 3: 振动光纤
/// 4: 泄漏电缆
/// 5: 网络或总线多子防区模块
/// </summary>
public int Type { get; set; }//防区类型,特殊参数需单独查询
public string TypeStr
{
get
{
return Type switch
{
0 => "普通防区",
1 => "张力防区",
2 => "脉冲围栏",
3 => "振动光纤",
4 => "泄漏电缆",
5 => "网络或总线多子防区模块",
_ => "未知类型:" + Type,
};
}
}
#endregion Fields
public SectorState(int deviceId, int id, byte data)
{
DeviceId = deviceId;
Id = id;
State = data & 0x0F;
Type = data >> 4;
}
}

158
JiLinApp.Docking/FenceAlarm/Entity/UdpAlarmHost.cs

@ -0,0 +1,158 @@
using System.Collections.Concurrent;
namespace JiLinApp.Docking.FenceAlarm;
public class UdpAlarmHost
{
public int DeviceId { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
public int GroupId { get; set; }//分组号,报警主机可划入不同组
public int UserId { get; set; }//用户ID,指主机名称,有多台主机可以通过此名称区分
/// <summary>
/// 0x00: 撤防状态
/// 0x01: 外出布防(普通布防最常用)
/// 0x02: 即时布防(所有防区没有延时)
/// 0x04: 在家布防(留守布防有些防区可能是在旁路状态)
/// 0x08: 即时留守布防(有些防区可能旁路, 但是没有旁路防区没有延时)
/// 0x09: 部分防区布防部分防区撤防
/// </summary>
public int DefenceState { get; set; }//主机布撤状态
public string DefenceStateStr
{
get
{
return DefenceState switch
{
0x00 => "撤防状态",
0x01 => "外出布防",
0x02 => "即时布防",
0x04 => "在家布防",
0x08 => "即时留守布防",
0x09 => "部分防区布防",
_ => "未知状态号:" + DefenceState,
};
}
}
/// <summary>
/// 0x00:此设备处于常规工作状态,正常报警
/// 0x01:工作在设置模式,不会处理报警,用户这进行设置,设置完一般很快退出
/// 0x02:用户正在查询当前报警信息
/// 0x03:用户正在设置时钟
/// 0x04:工作在用户密码修改模式下
/// </summary>
public int WorkState { get; set; }//设备工作状态
public string WorkStateStr
{
get
{
return DefenceState switch
{
0x00 => "常规工作状态",
0x01 => "在设置模式",
0x02 => "用户正在查询报警",
0x03 => "用户正在设置时钟",
0x04 => "用户密码修改模式",
_ => "未知状态号:" + WorkState,
};
}
}
public int CellState { get; set; }//电池状态 1=电池故障 0=电池正常
public int ElectricityState { get; set; }//交流状态 1=交流掉电 0=交流正常
public int FuseState { get; set; }//警保险丝状态 1=外接警号断了0=正常
public int SectorState { get; set; }//有线防区触发了 1=其中有线防区触发了 0=所有有线防区准备好
public int CellTestState { get; set; }//正在电池载能测试 1=主机正在进行电池载能测试 0=无(主机会降低电压到电池12V以下,来测试电池负载特性需要2分钟)
public int DeviceElectricityState { get; set; }//设备交流电状态1=交流掉电,目前电池供电0=正常 这是即时状态
public int SoundState { get; set; }//设备声音输出状态(=0关闭声音) =1 输出声音) 用于布防和报警延时
public int TestState { get; set; }//主机本地测试状态1=主机在本地测试(用于测试探测好坏是没有报警记录的)0=无
public int CriticalAlarmState { get; set; }//软防区紧急报警状态1=有紧急报警(用户通过手动按下主机自带装置报警) 0=无
public int FireAlarmState { get; set; }//软防区火警报警状态1=有火警报警(用户通过手动按下主机自带装置报警) 0=无
public int StealAlarmState { get; set; }//软防区盗警报警状态1=有盗警报警(用户通过手动按下主机自带装置报警) 0=无
public int DeviceFireAlarmState { get; set; }//设备中有火警报警状态 1=其中有火警防区触发了 0=无 整体状态
public int DeviceAlarmState { get; set; }//设备有报警发生 1=指设备有报警状态,任一一种0=无报警
public int DeviceBywayState { get; set; }//设备有旁路防区1=指设备某些防区有旁路了(这时软件可以要求上传具体旁路的防区) 2=无防区旁路
public int BusOfflineState { get; set; }//有总线防区掉线状态1=指某些总线防区掉线了0=无
public int NetOfflineState { get; set; }//有网络防区掉线状态1=指某些网络防区掉线了0=无
/// <summary>
/// 00: 通用设备兼容没有设置或未知设备
/// 01: EH508报警主机
/// 02: EH508NP网络模块
/// 03: NETLINKBOARD32路网络模块
/// 04: EH508NETCILENT网络终端
/// 05: EH8008路光纤主机
/// 06: EH508NP_UDP私有服务器网络模块
/// 07: EH508CILENT_UDP私有服务器接警终端
/// 08: EH508SEVER_UDP私有服务器主机
/// 09: EH508MAP_UDP私有服务器电子地图
/// 0A: EH508_UDP私有服务器用EH508主机
/// 0x32: H768_IP系列网络模块
/// 0x33: D238C_IP网络模块
/// 0x34: H778S+系列报警主机
/// 0x35: H778S系列报警主机
/// 0x36: N201N202网络模块
/// </summary>
public int DevideType { get; set; }//设备类型:区分不同的设备类型 增强可控,防止混乱,具体的设备类型见附加说明表格
public int SignalIntensity { get; set; }//31:信号最强,如果设备类型不是这种不需要显示 指主要带有GPRS模块设备跟据设备类型来的
//-------------------------------防区信息-----------------------------//
public int SectorTotal { get; set; }//分区总数量
public ConcurrentDictionary<int, SectorState> SectorDict { get; }
public bool SectorsEmpty
{
get
{
return SectorDict == null || SectorDict.IsEmpty;
}
}
public ReaderWriterLockSlim SectorsLock { get; } = new();
public int OnlineState { get; set; }//设备在线状态
public int KeepLive { get; set; }//设备在线状态
public UdpAlarmHost()
{
DeviceId = 0;
GroupId = 0;
UserId = 0;
DefenceState = 0;
WorkState = 0;
CellState = 0;
ElectricityState = 0;
FuseState = 0;
SectorState = 0;
CellTestState = 0;
DeviceElectricityState = 0;
SoundState = 0;
TestState = 0;
CriticalAlarmState = 0;
FireAlarmState = 0;
StealAlarmState = 0;
DeviceFireAlarmState = 0;
DeviceAlarmState = 0;
DeviceBywayState = 0;
BusOfflineState = 0;
NetOfflineState = 0;
DevideType = 0;
SignalIntensity = 0;
SectorTotal = 0;
OnlineState = 0;
SectorDict = new();
}
}

29
JiLinApp.Docking/FenceAlarm/Entity/UdpAlarmHostMessage.cs

@ -0,0 +1,29 @@
using Newtonsoft.Json.Linq;
namespace JiLinApp.Docking.FenceAlarm;
public class UdpAlarmHostMessage
{
public string AlarmTime { get; set; }//报警时间
public string CID { get; set; }//CID代码
/*设备信息*/
public int DeviceId { get; set; }//设备唯一ID
public string Ip { get; set; }//设备IP
public int SectorId { get; set; }//防区号
public int SubSectorId { get; set; }//子防区号
public int GroupId { get; set; }//分组号,报警主机可划入不同组
public int UserId { get; set; }//用户ID,指主机名称,有多台主机可以通过此名称区分
public int LinkOut { get; set; }//联动输出
public string ExtendArgs { get; set; }//扩展参数
/*CID信息*/
public string AlarmLevel { get; set; }//报警级别
public string AlarmContent { get; set; }//报警内容
public string AlarmRemarks { get; set; }//报警备注
public string AlarmType { get; set; }//报警类型
/*联动信息*/
public bool IsLinked { get; set; }//是否有联动信息
public JArray Linklist { get; set; }
}

31
JiLinApp.Docking/FenceAlarm/Entity/UdpDatagramReceivedEventArgs.cs

@ -0,0 +1,31 @@
using System.Net;
namespace JiLinApp.Docking.FenceAlarm;
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <typeparam name="T">报文类型</typeparam>
public class UdpDatagramReceivedEventArgs<T> : EventArgs
{
/// <summary>
/// 客户端
/// </summary>
public IPEndPoint Ipep { get; private set; }
/// <summary>
/// 报文
/// </summary>
public T Datagram { get; private set; }
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public UdpDatagramReceivedEventArgs(IPEndPoint ipep, T datagram)
{
Ipep = ipep ?? throw new ArgumentNullException(nameof(ipep));
Datagram = datagram;
}
}

10
JiLinApp.Docking/FenceAlarm/Entity/UdpManagerConfig.cs

@ -0,0 +1,10 @@
namespace JiLinApp.Docking.FenceAlarm;
public class UdpManagerConfig
{
public string ServerIp { get; set; }
public int ServerPort { get; set; }
public int DeviceHeartKeep { get; set; }
}

256
JiLinApp.Docking/FenceAlarm/Service/AlarmEncode.cs

@ -0,0 +1,256 @@
namespace JiLinApp.Docking.FenceAlarm;
public class AlarmEncode
{
#region Fields
public static readonly byte Version = 0x12;
public static readonly byte[] Head = new byte[] { 0xF0, 0xFA };
public static readonly byte End = 0x0D;
public static readonly byte[] TEA_key = new byte[16] {
0x08,0x01,0x08,0x06,0x07,0x08,0x07,0x08,
0x08,0x90,0xC5,0x04,0x0D,0x0E,0x0F,0x10
};
#endregion Fields
public static byte[] EncodeMessage(byte[] msg)
{
if (!CheckMessage(msg)) return Array.Empty<byte>();
return GetMessage(msg[0], BteaEncrypt(GetContent(msg, msg[0] - 1)));
}
public static byte[] DecodeMessage(byte[] msg)
{
if (!CheckMessage(msg)) return Array.Empty<byte>();
return GetMessage(msg[0], BteaDecrpyt(GetContent(msg, msg[0] - 1)));
}
private static bool CheckMessage(byte[] msg)
{
if (msg == null) return false;
if (msg[0] > msg.Length) return false;
return true;
}
private static byte[] GetMessage(byte msgLen, byte[] msg)
{
byte[] outMsg = new byte[msg.Length + 1];
outMsg[0] = msgLen;
for (int i = 0; i < msg.Length; i++)
{
outMsg[i + 1] = msg[i];
}
return outMsg;
}
private static byte[] GetContent(byte[] msg, int len)
{
byte[] bytes = new byte[len];
for (int i = 0; i < len; i++)
{
bytes[i] = msg[i + 1];
}
return bytes;
}
public static byte[] GetSendMessage(byte command, byte[] data)
{
byte[] msg = data != null ? new byte[data.Length + 8] : new byte[8];
msg[0] = (byte)msg.Length;
msg[1] = Version;
msg[2] = Head[0];
msg[3] = Head[1];
msg[4] = command;
if (data != null)
{
for (int i = 0; i < data.Length; i++)
{
msg[i + 5] = data[i];
}
}
byte[] subMsg = data != null ? new byte[data.Length + 4] : new byte[4];
for (int i = 0; i < subMsg.Length; i++)
{
subMsg[i] = msg[i + 1];
}
byte[] crc = CRC16(subMsg);
msg[^3] = crc[1];
msg[^2] = crc[0];
msg[^1] = End;
return msg;
}
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len > 0)
{
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ (data[i]));
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
byte lo = (byte)(crc & 0x00FF); //低位置
return new byte[] { hi, lo };
}
return new byte[] { 0, 0 };
}
private static byte[] BteaEncrypt(byte[] bytes)
{
byte[] output = new byte[bytes.Length];
for (int i = 0; i < bytes.Length; i++)
{
byte abyte = (byte)(bytes[i] ^ TEA_key[i % 16]);
output[i] = (byte)(((byte)(abyte >> 3)) | ((byte)(abyte << 5)));
}
return output;
}
private static byte[] BteaDecrpyt(byte[] bytes)
{
byte[] output = new byte[bytes.Length];
for (int i = 0; i < bytes.Length; i++)
{
byte abyte = (byte)(((byte)(bytes[i] << 3)) | ((byte)(bytes[i] >> 5)));
output[i] = (byte)(abyte ^ TEA_key[i % 16]);
}
return output;
}
}
public enum DeviceCmd : byte
{
/// <summary>
/// 心跳
/// </summary>
HeartBeatCmd = 0x01,
/// <summary>
/// 报警
/// </summary>
AlarmCmd = 0x02,
/// <summary>
/// 所有防区状态
/// </summary>
AllSectorStateCmd = 0x03,
/// <summary>
/// 实时防区状态
/// </summary>
RtSectorStateCmd = 0x04,
/// <summary>
/// 最大防区号
/// </summary>
MaxSectorTotalCmd = 0x05,
/// <summary>
/// 报警主机设置参数返回服务器
/// </summary>
BackSettingsCmd = 0x08,
/// <summary>
/// 布防指令
/// </summary>
DefenceCmd = 0x80,
/// <summary>
/// 校对时间指令
/// </summary>
CheckTimeCmd = 0x8D,
/// <summary>
/// 正确应答指令
/// </summary>
OkCmd = 0x8F,
/// <summary>
/// 主机重启指令
/// </summary>
RebootCmd = 0xA0,
}
public enum DeviceSectorType : byte
{
/// <summary>
/// 防区未使用
/// </summary>
NotUsed = 0x00,
/// <summary>
/// 防区撤防
/// </summary>
Withdraw = 0x01,
/// <summary>
/// 防区布防
/// </summary>
Deploy = 0x02,
/// <summary>
/// 防区旁路
/// </summary>
Byway = 0x03,
/// <summary>
/// 防区报警
/// </summary>
Alarm = 0x04,
/// <summary>
/// 无线防区欠压
/// </summary>
Undervoltage = 0x05,
/// <summary>
/// 防区掉线(与主线总线脱离)
/// </summary>
OffBus = 0x06,
/// <summary>
/// 未准备就绪
/// </summary>
Unprepared = 0x07,
}
public enum DeviceDefenceState : byte
{
/// <summary>
/// 撤防状态
/// </summary>
Withdraw = 0x00,
/// <summary>
/// 外出布防(普通布防最常用)
/// </summary>
GoOut = 0x01,
/// <summary>
/// 即时布防(所有防区没有延时)
/// </summary>
Immediately = 0x02,
/// <summary>
/// 在家布防(留守布防有些防区可能是在旁路状态)
/// </summary>
Home = 0x04,
/// <summary>
/// 即时留守布防(有些防区可能旁路,但是没有旁路防区没有延时)
/// </summary>
HomeImmediately = 0x08,
/// <summary>
/// 部分防区布防部分防区撤防
/// </summary>
Part = 0x09
}

793
JiLinApp.Docking/FenceAlarm/Service/UdpManager.cs

@ -0,0 +1,793 @@
using JiLinApp.Docking.VibrateAlarm;
using System.Collections.Concurrent;
using System.Net;
using System.Timers;
using Timer = System.Timers.Timer;
namespace JiLinApp.Docking.FenceAlarm;
public class UdpManager
{
#region Fields
private UdpServer Server { get; set; }
private UdpManagerConfig Config { get; set; }
private Timer PtzCheckTimer { get; } = new();
private ConcurrentDictionary<int, UdpAlarmHost> DeviceDict { get; } = new();
#region Event
public delegate void FenceUdpDeviceStateEvent(UdpAlarmHost device);
public delegate void FenceUdpSectorStateEvent(SectorState sector);
public delegate void FenceUdpAlarmEvent(UdpAlarmHostMessage msg);
public event FenceUdpDeviceStateEvent? OnFenceUdpDeviceState;
public event FenceUdpSectorStateEvent? OnFenceUdpSectorState;
public event FenceUdpAlarmEvent? OnFenceUdpAlarm;
#endregion Event
#endregion Fields
public UdpManager()
{
}
#region Server
public void Start(UdpManagerConfig config)
{
if (IsRunning()) return;
Server = new(config.ServerPort);
Server.DatagramReceived += Server_DatagramReceived;
Server.Start();
PtzCheckTimer.Interval = 3000;//3s
PtzCheckTimer.Elapsed += PTZCheckTimer_Elapsed;
PtzCheckTimer.Enabled = true;
Config = config;
}
public void Stop()
{
if (!IsRunning()) return;
try
{
Server.Stop();
}
finally
{
Server.DatagramReceived -= Server_DatagramReceived;
Server = null;
DeviceDict.Clear();
PtzCheckTimer.Stop();
PtzCheckTimer.Elapsed -= PTZCheckTimer_Elapsed;
}
}
public bool IsRunning()
{
return Server != null && Server.IsRunning();
}
#endregion Server
#region Events
/// <summary>
/// 计时判断设备是否在线,若超过规定时间没有新消息发来,则设备离线
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PTZCheckTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
foreach (var key in DeviceDict.Keys)
{
UdpAlarmHost device = DeviceDict[key];
if (device.KeepLive > 0) device.KeepLive--;
else ProcessDeviceStateEvent(ref device, 0, device.DefenceState);
}
}
private void Server_DatagramReceived(object? sender, UdpDatagramReceivedEventArgs<byte[]> e)
{
IPEndPoint ipep = e.Ipep;
//解码
//byte[] msg = e.Datagram;
byte[] msg = AlarmEncode.DecodeMessage(e.Datagram);
bool vaild = msg.Length >= 8 && msg[2] == AlarmEncode.Head[0] && msg[3] == AlarmEncode.Head[1];
Console.WriteLine("Recv from {0}:{1} => {2}, {3}, {4}", ipep.Address.ToString(), ipep.Port, DataMessage.ToHexString(e.Datagram), DataMessage.ToHexString(msg), vaild);
if (!vaild) return;
//解析
DeviceCmd deviceCmd = (DeviceCmd)msg[4];
switch (deviceCmd)
{
case DeviceCmd.HeartBeatCmd://心跳信息
AnalysisHeartMessage(ipep, msg);
break;
case DeviceCmd.AlarmCmd://报警信息
AnalysisAlarmMessage(ipep, msg);
break;
case DeviceCmd.AllSectorStateCmd://防区信息
AnalysisAllSectorMessage(ipep, msg);
break;
case DeviceCmd.RtSectorStateCmd://张力防区信息
AnalysisSectorMessage(ipep, msg);
break;
case DeviceCmd.MaxSectorTotalCmd://最大防区信息
AnalysisMaxSectorMessage(ipep, msg);
break;
case DeviceCmd.BackSettingsCmd://返回报警主机设置参数回服务器,无需解析
break;
default:
break;
}
SendOk(ipep);
}
#endregion Events
#region Analysis
private void AnalysisHeartMessage(IPEndPoint ipep, byte[] msg)
{
string ip = ipep.Address.ToString();
int deviceId = ByteToInt(msg, 5);
if (!TryGetDevice(deviceId, out UdpAlarmHost device))
{
device = new()
{
DeviceId = deviceId,
Ip = ip,
Port = ipep.Port,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
OnlineState = 1,
KeepLive = Config.DeviceHeartKeep
};
AddDevice(deviceId, ref device);
}
byte defenceState = msg[17];
//在线状态
device.KeepLive = Config.DeviceHeartKeep;
ProcessDeviceStateEvent(ref device, 1, defenceState);
device.WorkState = msg[18];
//设备状态1
device.CellState = GetBit(msg[19], 0);
device.ElectricityState = GetBit(msg[19], 1);
device.FuseState = GetBit(msg[19], 2);
device.SectorState = GetBit(msg[19], 3);
device.CellTestState = GetBit(msg[19], 4);
device.DeviceElectricityState = GetBit(msg[19], 5);
device.SoundState = GetBit(msg[19], 6);
device.TestState = GetBit(msg[19], 7);
//设备状态2
device.CriticalAlarmState = GetBit(msg[20], 0);
device.FireAlarmState = GetBit(msg[20], 1);
device.StealAlarmState = GetBit(msg[20], 2);
device.DeviceFireAlarmState = GetBit(msg[20], 3);
device.DeviceAlarmState = GetBit(msg[20], 4);
device.DeviceBywayState = GetBit(msg[20], 5);
device.BusOfflineState = GetBit(msg[20], 6);
device.NetOfflineState = GetBit(msg[20], 7);
//设备状态3,4暂不使用
device.DevideType = msg[23];
device.SignalIntensity = msg[24];
device.SectorTotal = msg[25];
}
private void AnalysisAlarmMessage(IPEndPoint ipep, byte[] msg)
{
string ip = ipep.Address.ToString();
int deviceId = ByteToInt(msg, 5);
if (!TryGetDevice(deviceId, out _))
{
UdpAlarmHost device = new()
{
DeviceId = deviceId,
Ip = ip,
Port = ipep.Port,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
OnlineState = 1,
KeepLive = Config.DeviceHeartKeep
};
AddDevice(deviceId, ref device);
}
string alarmTime = $"20{GetBCD(msg[17])}-{GetBCD(msg[18])}-{GetBCD(msg[19])} " +
$"{GetBCD(msg[20])}:{GetBCD(msg[21])}:{GetBCD(msg[22])}";
string CID = GetCID(msg[23]) + GetCID(msg[24]) + GetCID(msg[25]) + GetCID(msg[26]);
int sectorId = msg[29] + msg[30] * 256;
UdpAlarmHostMessage alarm = new()
{
DeviceId = deviceId,
Ip = ip,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
AlarmTime = alarmTime,
//CID暂定
CID = CID,
LinkOut = msg[27] + msg[28] * 256,
SectorId = sectorId,
SubSectorId = msg[31] + msg[32] * 256,
ExtendArgs = msg[33].ToString("X2") + " " + msg[34].ToString("X2") + " " + msg[35].ToString("X2"),
};
ReportAlarm(alarm);
}
private void AnalysisAllSectorMessage(IPEndPoint ipep, byte[] msg)
{
string ip = ipep.Address.ToString();
int deviceId = ByteToInt(msg, 5);
if (!TryGetDevice(deviceId, out UdpAlarmHost device))
{
device = new()
{
DeviceId = deviceId,
Ip = ip,
Port = ipep.Port,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
OnlineState = 1,
KeepLive = Config.DeviceHeartKeep
};
AddDevice(deviceId, ref device);
}
device.SectorTotal = msg[17] * 256 + msg[18];
int sectorNum = msg[19], startIndex = msg[20];
for (int i = 0; i < sectorNum; i++)
{
int sectorId = i + startIndex;//防区序号
int pos = 21 + i;//防区信息所在byte数组未知
SectorState curSector = new(device.DeviceId, sectorId, msg[pos]);
if (device.SectorDict.TryGetValue(sectorId, out SectorState? sector))
{
ProcessSectorStateEvent(ref sector, curSector.State);
}
else
{
sector = curSector;
device.SectorDict[sector.Id] = sector;
//ProcessSectorStateEvent(ref sector);
}
}
}
private void AnalysisSectorMessage(IPEndPoint ipep, byte[] msg)
{
//东北没有张力防区,暂不解析
string ip = ipep.Address.ToString();
int deviceId = ByteToInt(msg, 5);
if (!TryGetDevice(deviceId, out UdpAlarmHost device))
{
device = new()
{
DeviceId = deviceId,
Ip = ip,
Port = ipep.Port,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
OnlineState = 1,
KeepLive = Config.DeviceHeartKeep
};
AddDevice(deviceId, ref device);
}
}
private void AnalysisMaxSectorMessage(IPEndPoint ipep, byte[] msg)
{
string ip = ipep.Address.ToString();
int deviceId = ByteToInt(msg, 5);
if (!TryGetDevice(deviceId, out UdpAlarmHost device))
{
device = new()
{
DeviceId = deviceId,
Ip = ip,
Port = ipep.Port,
GroupId = ByteToInt(msg, 9),
UserId = ByteToInt(msg, 13),
OnlineState = 1,
KeepLive = Config.DeviceHeartKeep
};
AddDevice(deviceId, ref device);
}
device.SectorTotal = msg[17] * 256 + msg[18];
}
private void ProcessDeviceStateEvent(ref UdpAlarmHost device, int onlineState, int defenceState)
{
bool reportFlag = false;
if (device.OnlineState != onlineState)
{
if (onlineState == 0 && device.KeepLive < 0)
{
reportFlag = true;
device.OnlineState = onlineState;
}
else if (onlineState == 1 && device.KeepLive >= 0)
{
reportFlag = true;
device.OnlineState = onlineState;
}
}
if (device.DefenceState != defenceState)
{
reportFlag = true;
device.DefenceState = defenceState;
}
if (reportFlag) ReportDeviceState(device);
}
private void ProcessSectorStateEvent(ref SectorState sector, int state)
{
if (sector.State != state)
{
sector.State = state;
ReportSectorState(sector);
}
}
private void ReportDeviceState(UdpAlarmHost device)
{
OnFenceUdpDeviceState?.Invoke(device);
}
private void ReportSectorState(SectorState sector)
{
OnFenceUdpSectorState?.Invoke(sector);
}
private void ReportAlarm(UdpAlarmHostMessage msg)
{
OnFenceUdpAlarm?.Invoke(msg);
}
#endregion Analysis
#region Send
/// <summary>
/// 应答
/// </summary>
/// <param name="ipep"></param>
/// <returns></returns>
private bool SendOk(IPEndPoint ipep)
{
if (!IsRunning()) return false;
byte[] bytes = new byte[] { 0x08, 0x12, 0xF0, 0xFA, 0x8F, 0x06, 0x6B, 0x0D };
////byte[] bytes = AlarmEncode.GetSendMessage(0x8F, Array.Empty<byte>());
return Server.SendMessage(ipep.Address.ToString(), ipep.Port, AlarmEncode.EncodeMessage(bytes));
//Console.WriteLine();
//byte[] bytes = new byte[] {
// 0x24, 0x12, 0xF0, 0xFA,
// 0x02,
// 0x77, 0x35, 0x94, 0x01,
// 0x07, 0x5B, 0xCD, 0x15,
// 0x00, 0x01, 0xE2, 0x40,
// 0x23, 0x05, 0x18, 0x17, 0x12, 0x34,
// 0x01, 0x00, 0x00, 0x01,
// 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
// 0x0F, 0x41, 0x0D };
//Console.WriteLine(DataMessage.ToHexString(AlarmEncode.EncodeMessage(bytes))); bytes = new byte[] {
// 0x24, 0x12, 0xF0, 0xFA,
// 0x02,
// 0x77, 0x35, 0x94, 0x01,
// 0x07, 0x5B, 0xCD, 0x15,
// 0x00, 0x01, 0xE2, 0x40,
// 0x23, 0x05, 0x18, 0x17, 0x12, 0x34,
// 0x01, 0x00, 0x00, 0x02,
// 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
// 0x3C, 0x05, 0x0D };
//Console.WriteLine(DataMessage.ToHexString(AlarmEncode.EncodeMessage(bytes))); bytes = new byte[] {
// 0x24, 0x12, 0xF0, 0xFA,
// 0x02,
// 0x77, 0x35, 0x94, 0x01,
// 0x07, 0x5B, 0xCD, 0x15,
// 0x00, 0x01, 0xE2, 0x40,
// 0x23, 0x05, 0x18, 0x17, 0x12, 0x34,
// 0x01, 0x00, 0x00, 0x01,
// 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
// 0x0E, 0x35, 0x0D };
//Console.WriteLine(DataMessage.ToHexString(AlarmEncode.EncodeMessage(bytes))); bytes = new byte[] {
// 0x24, 0x12, 0xF0, 0xFA,
// 0x02,
// 0x77, 0x35, 0x94, 0x01,
// 0x07, 0x5B, 0xCD, 0x15,
// 0x00, 0x01, 0xE2, 0x40,
// 0x23, 0x05, 0x18, 0x17, 0x12, 0x34,
// 0x01, 0x00, 0x00, 0x02,
// 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
// 0x3C, 0xC9, 0x0D };
//Console.WriteLine(DataMessage.ToHexString(AlarmEncode.EncodeMessage(bytes)));
//Server.SendMessage(ipep.Address.ToString(), ipep.Port, AlarmEncode.EncodeMessage(bytes));
//return true;
}
/// <summary>
/// 外出布防或者布防
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool SetDeviceDefence(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x60 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 撤防
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool WithdrawDeviceDefence(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x61 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 立即布防
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool SetDeviceDefenceImmediately(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x62 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 在家布防留守布防
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool SetDeviceDefenceHome(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x63 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 即时留守布防
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool SetDeviceDefenceHomeImmediately(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x64 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 清除报警记忆(复位)
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool ClearDeviceDefence(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x80, new byte[] { 0x65 });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 校对系统时间
/// </summary>
/// <param name="deviceId"></param>
/// <param name="time"></param>
/// <returns></returns>
public bool SetDeviceTime(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] content = GetBCDTime(DateTime.Now);
byte[] bytes = AlarmEncode.GetSendMessage(0x8D, content);
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 设备重启
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool RebootDevice(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0xA0, Array.Empty<byte>());
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 单防区布防
/// </summary>
/// <param name="deviceId"></param>
/// <param name="sectorId"></param>
/// <returns></returns>
public bool SetSectorefence(int deviceId, int sectorId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0xC0, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 单防区撤防
/// </summary>
/// <param name="deviceId"></param>
/// <param name="sectorId"></param>
/// <returns></returns>
public bool WithdrawSectorDefence(int deviceId, int sectorId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0xC1, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 单防区旁路
/// </summary>
/// <param name="deviceId"></param>
/// <param name="sectorId"></param>
/// <returns></returns>
public bool SetSectorByway(int deviceId, int sectorId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0xC2, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 单防区旁路恢复
/// </summary>
/// <param name="deviceId"></param>
/// <param name="sectorId"></param>
/// <returns></returns>
public bool WithdrawSectorByway(int deviceId, int sectorId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0xC3, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 查询防区状态
/// </summary>
/// <param name="deviceId"></param>
/// <param name="sectorId"></param>
/// <returns></returns>
public bool SearchSectorState(int deviceId, int sectorId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
byte[] bytes = AlarmEncode.GetSendMessage(0x85, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
/// <summary>
/// 查询所有防区状态
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public bool SearchAllSectorState(int deviceId)
{
UdpAlarmHost device = GetDevice(deviceId);
if (CheckDevice(device))
{
int sectorId = 0x00;
byte[] bytes = AlarmEncode.GetSendMessage(0x85, new byte[] { (byte)(sectorId / 256), (byte)(sectorId % 256) });
return Server.SendMessage(device.Ip, device.Port, AlarmEncode.EncodeMessage(bytes));
}
return false;
}
#endregion Send
#region DeviceDict
public bool ContainsDevice(int deviceId)
{
return DeviceDict.ContainsKey(deviceId);
}
public UdpAlarmHost GetDevice(int deviceId)
{
return DeviceDict[deviceId];
}
public bool TryGetDevice(int deviceId, out UdpAlarmHost device)
{
return DeviceDict.TryGetValue(deviceId, out device);
}
private bool AddDevice(int deviceId, ref UdpAlarmHost device)
{
if (ContainsDevice(deviceId)) return false;
DeviceDict[deviceId] = device;
UdpAlarmHost innerDevice = DeviceDict[deviceId];
if (innerDevice.SectorsEmpty && innerDevice.SectorsLock.TryEnterWriteLock(1000))
{
Task.Run(() =>
{
innerDevice = DeviceDict[deviceId];
while (innerDevice.SectorsEmpty)
{
SearchAllSectorState(innerDevice.DeviceId);
Thread.Sleep(1000);
}
innerDevice.SectorsLock.ExitWriteLock();
});
}
return true;
}
private void SetDevice(int deviceId, UdpAlarmHost device)
{
DeviceDict[deviceId] = device;
}
private bool RemoveDevice(int deviceId)
{
return DeviceDict.Remove(deviceId, out _);
}
public List<UdpAlarmHost> GetDeviceList()
{
return DeviceDict.Values.ToList();
}
private bool CheckDevice(UdpAlarmHost device)
{
if (!IsRunning()) return false;
if (device == null) return false;
if (device.OnlineState == 0) return false;
return true;
}
#endregion DeviceDict
#region Util
private int GetBit(byte bytes, int index)
{
return index switch
{
0 => bytes & 0x01,
1 => (bytes & 0x02) >> 1,
2 => (bytes & 0x04) >> 2,
3 => (bytes & 0x08) >> 3,
4 => (bytes & 0x10) >> 4,
5 => (bytes & 0x20) >> 5,
6 => (bytes & 0x40) >> 6,
7 => (bytes & 0x80) >> 7,
_ => 0,
};
}
private int ByteToInt(byte[] msg, int start)
{
byte[] bytes = new byte[] { msg[start + 3], msg[start + 2], msg[start + 1], msg[start] };
return BitConverter.ToInt32(bytes, 0);
}
private byte[] IntToByte(int num)
{
byte[] bytes = BitConverter.GetBytes(num);
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
private string GetCID(byte bytes)
{
return bytes switch
{
0x00 => "0",
0x01 => "1",
0x02 => "2",
0x03 => "3",
0x04 => "4",
0x05 => "5",
0x06 => "6",
0x07 => "7",
0x08 => "8",
0x09 => "9",
0x0A => "A",
0x0B => "B",
0x0C => "C",
0x0D => "D",
0x0E => "E",
0x0F => "F",
_ => "0",
};
}
private string GetBCD(byte bytes)
{
int num = (bytes >> 4) * 10 + (bytes & 0x0F);
return num.ToString();
}
private byte GetBCDByte(int num)
{
if (num >= 100) num %= 100;
int hex = num / 10;
int lex = num % 10;
return (byte)(hex * 16 + lex);
}
private byte[] GetBCDTime(DateTime time)
{
return new byte[] { GetBCDByte(time.Year),GetBCDByte((int)time.DayOfWeek),GetBCDByte(time.Month),GetBCDByte(time.Day),
GetBCDByte(time.Hour),GetBCDByte(time.Minute),GetBCDByte(time.Second) };
}
#endregion Util
}

136
JiLinApp.Docking/FenceAlarm/Service/UdpServer.cs

@ -0,0 +1,136 @@
using JiLinApp.Docking.VibrateAlarm;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
namespace JiLinApp.Docking.FenceAlarm;
public class UdpServer
{
#region Fields
/// <summary>
/// 用于UDP接收的网络服务类
/// </summary>
private UdpClient RecvUdp { get; }
/// <summary>
/// 用于UDP发送的网络服务类
/// </summary>
private UdpClient SendUdp { get; }
private BackgroundWorker Worker { get; }
#region Properties
public string RecvIp { get; private set; }
public int RecvPort { get; private set; }
#endregion Properties
#endregion Fields
#region Ctors
public UdpServer(int port) : this("0.0.0.0", port)
{
}
public UdpServer(string ip, int port)
{
RecvIp = ip;
RecvPort = port;
RecvUdp = new(new IPEndPoint(IPAddress.Parse(ip), port));
SendUdp = new(new IPEndPoint(IPAddress.Any, 0));
Worker = new()
{
WorkerSupportsCancellation = true
};
Worker.DoWork += Back_DoWork;
}
~UdpServer()
{
Stop();
}
#endregion Ctors
#region Server
public void Start()
{
if (IsRunning()) return;
Worker.RunWorkerAsync();
}
public void Stop()
{
if (!IsRunning()) return;
Worker.CancelAsync();
RecvUdp.Close();
SendUdp.Close();
}
public bool IsRunning()
{
return Worker != null && Worker.IsBusy;
}
#endregion Server
#region Events
public event EventHandler<UdpDatagramReceivedEventArgs<byte[]>>? DatagramReceived;
private void RaiseDatagramReceived(IPEndPoint ipep, byte[] datagram)
{
DatagramReceived?.Invoke(this, new UdpDatagramReceivedEventArgs<byte[]>(ipep, datagram));
}
#endregion Events
#region Receive
private void Back_DoWork(object? sender, DoWorkEventArgs e)
{
IPEndPoint remoteIpep = new(IPAddress.Any, 0);
while (Worker != null && !Worker.CancellationPending)
{
try
{
byte[] bytRecv = RecvUdp.Receive(ref remoteIpep);
RaiseDatagramReceived(remoteIpep, bytRecv);
}
catch
{
}
}
}
#endregion Receive
#region Send
public bool SendMessage(string ip, int port, byte[] msg)
{
IPEndPoint ipep = new(IPAddress.Parse(ip), port);
int result = SendUdp.Send(msg, msg.Length, ipep);
bool flag = result == msg.Length;
Console.WriteLine("Send to {0}:{1} => {2}, {3}, {4}", ip, port, DataMessage.ToHexString(msg), DataMessage.ToHexString(AlarmEncode.DecodeMessage(msg)), flag);
return flag;
}
public bool SendMessage(IPEndPoint ipep, byte[] msg)
{
string ip = ipep.Address.ToString();
int port = ipep.Port;
int result = SendUdp.Send(msg, msg.Length, ipep);
bool flag = result == msg.Length;
Console.WriteLine("Send to {0}:{1} => {2}, {3}", ip, port, DataMessage.ToHexString(AlarmEncode.DecodeMessage(msg)), flag);
return flag;
}
#endregion Send
}

25
JiLinApp.Docking/JiLinApp.Docking.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Flurl" Version="3.0.7" />
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="NewLife.Core" Version="10.3.2023.503" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EC.Util\EC.Util.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="config\alarmcode.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

16
JiLinApp.Docking/Military/Config.cs

@ -0,0 +1,16 @@
namespace JiLinApp.Docking.Military;
public class MilitaryConfig
{
//public bool Enable { get; set; }
public string Url { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int RequestTryTime { get; set; }
public int RequestTryInterval { get; set; }
}

18
JiLinApp.Docking/Military/Entity/CameraLinkageInfo.cs

@ -0,0 +1,18 @@
namespace JiLinApp.Docking.Military;
public class CameraLinkageInfo
{
#region Fields
public string Id { get; set; }
public string DeviceId { get; set; }
public string SensorId { get; set; }
public string CameraId { get; set; }
public int[] PresetIds { get; set; }
#endregion Fields
}

165
JiLinApp.Docking/Military/MilitaryService.cs

@ -0,0 +1,165 @@
using EC.Util.CameraSDK;
using EC.Util.Common;
using Flurl.Http;
using Newtonsoft.Json.Linq;
namespace JiLinApp.Docking.Military;
public class MilitaryService
{
#region Fields
private MilitaryConfig Config { get; }
private string Token { get; set; }
#endregion Fields
public MilitaryService(MilitaryConfig config)
{
Config = config;
Token = Login();
}
#region Base
private string GetUrl()
{
return Config.Url;
}
private IFlurlRequest WithToken(string url)
{
return url.WithHeader("X-Access-Token", Token);
}
#endregion Base
#region Cmd
public string Login()
{
string url = $"{GetUrl()}/sys/login";
object data = new
{
username = Config.UserName,
password = Config.Password,
};
JObject response = new();
for (int i = 0; i < Config.RequestTryTime; i++)
{
response = WithToken(url).PostJsonAsync(data).ReceiveJson<JObject>().Result;
bool success = response["success"].ToBoolean();
if (!success)
{
Thread.Sleep(Config.RequestTryInterval);
continue;
}
string? token = response["result"]?["token"]?.ToString();
return token ?? string.Empty;
}
string? message = response["message"]?.ToString();
throw new Exception(message);
}
public List<CameraInfo> GetCameraList()
{
string url = $"{GetUrl()}/camera/setting/list";
JObject response = new();
for (int i = 0; i < Config.RequestTryTime; i++)
{
response = WithToken(url).GetAsync().ReceiveJson<JObject>().Result;
bool success = response["success"].ToBoolean();
if (!success)
{
Thread.Sleep(Config.RequestTryInterval);
continue;
}
JToken records = response?["result"]?["records"] ?? new JObject();
List<CameraInfo> list = new();
foreach (var item in records)
{
CameraManufactor manufactor;
string factory = item?["factory_dictText"]?.ToString() ?? string.Empty;
if (factory.Contains("海康威视")) manufactor = CameraManufactor.HiK;
else if (factory.Contains("大华")) manufactor = CameraManufactor.DaHua;
else if (factory.Contains("宇视")) manufactor = CameraManufactor.YuShi;
else continue;
string id = item?["id"]?.ToString() ?? string.Empty;
string ip = item?["ip"]?.ToString() ?? string.Empty;
string username = item?["user"]?.ToString() ?? string.Empty;
string password = item?["password"]?.ToString() ?? string.Empty;
string name = $"{item?["siteName"]?.ToString()}-{item?["cameraName"]?.ToString()}-{ip}";
if (VerifyUtil.IsEmpty(id) || !VerifyUtil.IsIp(ip) || VerifyUtil.IsEmpty(username) || VerifyUtil.IsEmpty(password)) continue;
CameraInfo info = CameraInfo.New(manufactor, ip, username, password);
info.Id = id;
info.Name = name;
list.Add(info);
}
return list;
}
string? message = response["message"]?.ToString();
throw new Exception(message);
}
public List<object> GetFencesInfoList()
{
string url = $"{GetUrl()}/msFencesInfo/list";
JObject response = new();
for (int i = 0; i < Config.RequestTryTime; i++)
{
response = WithToken(url).GetAsync().ReceiveJson<JObject>().Result;
bool success = response["success"].ToBoolean();
if (!success)
{
Thread.Sleep(Config.RequestTryInterval);
continue;
}
return new List<object>();
}
string? message = response["message"]?.ToString();
throw new Exception(message);
}
public List<CameraLinkageInfo> GetCameraLinkageList()
{
string url = $"{GetUrl()}/military/MsCameraLinkage/list";
JObject response = new();
for (int i = 0; i < Config.RequestTryTime; i++)
{
response = WithToken(url).GetAsync().ReceiveJson<JObject>().Result;
bool success = response["success"].ToBoolean();
if (!success)
{
Thread.Sleep(Config.RequestTryInterval);
continue;
}
JToken records = response?["result"]?["records"] ?? new JObject();
List<CameraLinkageInfo> list = new();
foreach (var item in records)
{
string id = item?["id"]?.ToString() ?? string.Empty;
//string deviceId = item?["deviceId"]?.ToString() ?? string.Empty;
string deviceId = "0";
string sensorId = item?["objCode"]?.ToString() ?? string.Empty;
string cameraId = item?["cameraId"]?.ToString() ?? string.Empty;
string placements = item?["placements"]?.ToString() ?? string.Empty;
int[] presetIds = Array.ConvertAll(placements.Split(","), int.Parse);
if (VerifyUtil.IsEmpty(id) || VerifyUtil.IsEmpty(sensorId) || VerifyUtil.IsEmpty(cameraId)) continue;
list.Add(new()
{
Id = id,
DeviceId = deviceId,
SensorId = sensorId,
CameraId = cameraId,
PresetIds = presetIds
});
}
return list;
}
string? message = response["message"]?.ToString();
throw new Exception(message);
}
#endregion Cmd
}

128
JiLinApp.Docking/Ptz/DCamera.cs

@ -0,0 +1,128 @@
namespace JiLinApp.Docking.Ptz;
public class DCamera
{
#region Fields
#region Data3
public static double SpeedMin = 1;
public static double SpeedMax = 60;
#endregion Data3
#endregion Fields
#region BaseMethod
private static byte[] CommandPara(byte com1, byte com2, int data1, int data2)
{
byte[] b = new byte[11];
b[0] = 0xA1;
int temp1 = data1;
int temp2 = data2;
b[1] = 0x00;
b[2] = 0x0B;
b[3] = com1;
b[4] = com2;
b[5] = (byte)(data1 >> 8 & 0xFF);
b[6] = (byte)(temp1 & 0xFF);
b[7] = (byte)(data2 >> 8 & 0xFF);
b[8] = (byte)(temp2 & 0xFF);
b[9] = (byte)(b[0] ^ b[1] ^ b[2] ^ b[3] ^ b[4] ^ b[5] ^ b[6] ^ b[7] ^ b[8]);
b[10] = 0xAF;
return b;
}
private static byte[] SendJoystick(double headSpeed, double pitchSpeed)
{
return CommandPara(0x4D, 0x58, (int)(headSpeed * 100), (int)(pitchSpeed * 100));
}
#endregion BaseMethod
#region PtzMethod
public static byte[] Left(double headSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = -SpeedMin;
if (absHs > SpeedMax) headSpeed = -SpeedMax;
return SendJoystick(headSpeed, 0);
}
public static byte[] Right(double headSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = SpeedMin;
if (absHs > SpeedMax) headSpeed = SpeedMax;
return SendJoystick(headSpeed, 0);
}
public static byte[] Up(double pitchSpeed)
{
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = SpeedMin;
if (absPs > SpeedMax) pitchSpeed = SpeedMax;
return SendJoystick(0, pitchSpeed);
}
public static byte[] Down(double pitchSpeed)
{
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = -SpeedMin;
if (absPs > SpeedMax) pitchSpeed = -SpeedMax;
return SendJoystick(0, pitchSpeed);
}
public static byte[] LeftUp(double headSpeed, double pitchSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = -SpeedMin;
if (absHs > SpeedMax) headSpeed = -SpeedMax;
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = SpeedMin;
if (absPs > SpeedMax) pitchSpeed = SpeedMax;
return SendJoystick(headSpeed, pitchSpeed);
}
public static byte[] LeftDown(double headSpeed, double pitchSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = -SpeedMin;
if (absHs > SpeedMax) headSpeed = -SpeedMax;
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = -SpeedMin;
if (absPs > SpeedMax) pitchSpeed = -SpeedMax;
return SendJoystick(headSpeed, pitchSpeed);
}
public static byte[] RightUp(double headSpeed, double pitchSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = SpeedMin;
if (absHs > SpeedMax) headSpeed = SpeedMax;
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = SpeedMin;
if (absPs > SpeedMax) pitchSpeed = SpeedMax;
return SendJoystick(headSpeed, pitchSpeed);
}
public static byte[] RightDown(double headSpeed, double pitchSpeed)
{
double absHs = Math.Abs(headSpeed);
if (absHs < SpeedMin) headSpeed = SpeedMin;
if (absHs > SpeedMax) headSpeed = SpeedMax;
double absPs = Math.Abs(pitchSpeed);
if (absPs < SpeedMin) pitchSpeed = -SpeedMin;
if (absPs > SpeedMax) pitchSpeed = -SpeedMax;
return SendJoystick(headSpeed, pitchSpeed);
}
public static byte[] Stop()
{
return SendJoystick(0, 0);
}
#endregion PtzMethod
}

240
JiLinApp.Docking/Ptz/PelcoD.cs

@ -0,0 +1,240 @@
namespace JiLinApp.Docking.Ptz;
public class PelcoD
{
private string watchdir = ""; //监控方向
private static readonly byte STX = 0xFF; //同步字节
#region 监控方向和定时监控实体
public string WatchDir
{
get { return watchdir; }
set { watchdir = value; }
}
#endregion 监控方向和定时监控实体
#region 基本指令定义
#region 指令码1
private const byte FocusNear = 0x01; //增加聚焦
private const byte IrisOpen = 0x02; //减小光圈
private const byte IrisClose = 0x04; //增加光圈
private const byte CameraOnOff = 0x08; //摄像机打开和关闭
private const byte AutoManualScan = 0x10; //自动和手动扫描
private const byte Sense = 0x80; //Sence码
#endregion 指令码1
#region 指令码2
private const byte PanRight = 0x02; //右
private const byte PanLeft = 0x04; //左
private const byte TiltUp = 0x08; //上
private const byte TiltDown = 0x10; //下
private const byte ZoomTele = 0x20; //增加对焦
private const byte ZoomWide = 0x40; //减小对焦
private const byte FocusFar = 0x80; //减小聚焦
#endregion 指令码2
#region 镜头左右平移的速度
public static byte PanSpeedMin = 0x00; //停止
public static byte PanSpeedMax = 0x3F; //最高速
#endregion 镜头左右平移的速度
#region 镜头上下移动的速度
public static byte TiltSpeedMin = 0x00; //停止
public static byte TiltSpeedMax = 0x3F; //最高速
#endregion 镜头上下移动的速度
#endregion 基本指令定义
private const byte PanRightUp = 0xa; //右上
private const byte PanLeftUp = 0x0c; //左上
private const byte PanRightDown = 0x12; //右下
private const byte PanLeftDown = 0x14; //左下
#region 云台控制枚举
public enum Switch
{
On = 0x01,
Off = 0x02
} //雨刷控制
public enum Focus
{
Near = FocusNear,
Far = FocusFar
} //聚焦控制
public enum Zoom
{
Wide = ZoomWide,
Tele = ZoomTele
} //对焦控制
public enum Tilt
{
Up = TiltUp,
Down = TiltDown
} //上下控制
public enum Pan
{
Left = PanLeft,
Right = PanRight,
LeftUp = PanLeftUp,
LeftDown = PanLeftDown,
RightUp = PanRightUp,
RightDown = PanRightDown
} //左右控制
public enum Scan
{
Auto,
Manual
} //自动和手动控制
public enum Iris
{
Open = IrisOpen,
Close = IrisClose
} //光圈控制
public enum PresetAction
{
Set = 0x03,
Clear = 0x05,
Goto = 0x07
}
#endregion 云台控制枚举
#region 云台控制方法
//雨刷控制
public static byte[] CameraSwitch(uint deviceAddress, Switch action)
{
byte m_action = CameraOnOff;
if (action == Switch.On)
m_action = CameraOnOff + Sense;
return Message.GetMessage(deviceAddress, m_action, 0x00, 0x00, 0x00);
}
//光圈控制
public static byte[] CameraIrisSwitch(uint deviceAddress, Iris action)
{
return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
}
//聚焦控制
public static byte[] CameraFocus(uint deviceAddress, Focus action)
{
if (action == Focus.Near)
return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
else
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
}
//对焦控制
public static byte[] CameraZoom(uint deviceAddress, Zoom action)
{
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
}
/// <summary>
/// 上下控制
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="action"></param>
/// <param name="speed"></param>
/// <returns></returns>
public static byte[] CameraTilt(uint deviceAddress, Tilt action, uint speed)
{
if (speed < TiltSpeedMin)
speed = TiltSpeedMin;
if (speed > TiltSpeedMax)
speed = TiltSpeedMax;
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, (byte)speed);
}
/// <summary>
/// 左右控制
/// </summary>
/// <param name="deviceAddress"></param>
/// <param name="action"></param>
/// <param name="speed"></param>
/// <returns></returns>
public static byte[] CameraPan(uint deviceAddress, Pan action, uint speed)
{
if (speed < PanSpeedMin)
speed = PanSpeedMin;
if (speed > PanSpeedMax)
speed = PanSpeedMax;
return Message.GetMessage(deviceAddress, 0x00, (byte)action, (byte)speed, 0x00);
}
//停止云台的移动
public static byte[] CameraStop(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x00, 0x00, 0x00);
}
//自动和手动控制
public static byte[] CameraScan(uint deviceAddress, Scan scan)
{
byte m_byte = AutoManualScan;
if (scan == Scan.Auto)
m_byte = AutoManualScan + Sense;
return Message.GetMessage(deviceAddress, m_byte, 0x00, 0x00, 0x00);
}
public static byte[] Preset(uint deviceAddress, PresetAction action, byte presetId)
{
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, presetId);
}
#endregion 云台控制方法
public struct Message
{
public static byte Address;
public static byte CheckSum;
public static byte Command1,
Command2,
Data1,
Data2;
public static byte[] GetMessage(
uint address,
byte command1,
byte command2,
byte data1,
byte data2
)
{
if (address < 1 & address > 256)
throw new Exception("Pelco D协议只支持256设备");
Address = byte.Parse(address.ToString());
Command1 = command1;
Command2 = command2;
Data1 = data1;
Data2 = data2;
CheckSum = (byte)(Address + Command1 + Command2 + Data1 + Data2);
return new byte[] { STX, Address, Command1, Command2, Data1, Data2, CheckSum };
}
}
}

337
JiLinApp.Docking/Ptz/PelcoP.cs

@ -0,0 +1,337 @@
namespace JiLinApp.Docking.Ptz;
/// <summary>
/// dot.NET Implementation of Pelco P Protocol
/// </summary>
public class PelcoP
{
private const byte STX = 0xA0;
private const byte ETX = 0xAF;
#region Pan and Tilt Commands
#region Data1
private const byte FocusFar = 0x01;
private const byte FocusNear = 0x02;
private const byte IrisOpen = 0x04;
private const byte IrisClose = 0x08;
private const byte CameraOnOff = 0x10;
private const byte AutoscanOn = 0x20;
private const byte CameraOn = 0x40;
#endregion Data1
#region Data2
private const byte PanRight = 0x02;
private const byte PanLeft = 0x04;
private const byte TiltUp = 0x08;
private const byte TiltDown = 0x10;
private const byte ZoomTele = 0x20;
private const byte ZoomWide = 0x40;
#endregion Data2
#region Data3
public static byte PanSpeedMin = 0x00;
public static byte PanSpeedMax = 0x3F;
#endregion Data3
#region Data4
public static byte TiltSpeedMin = 0x00;
public static byte TiltSpeedMax = 0x3F;
#endregion Data4
#endregion Pan and Tilt Commands
#region Enums
public enum PatternAction
{
Start,
Stop,
Run
}
public enum Action
{
Start,
Stop
}
public enum LensSpeed
{
Low = 0x00,
Medium = 0x01,
High = 0x02,
Turbo = 0x03
}
public enum Pan
{
Left = PanLeft,
Right = PanRight
}
public enum Tilt
{
Up = TiltUp,
Down = TiltDown
}
public enum Iris
{
Open = IrisOpen,
Close = IrisClose
}
public enum Zoom
{
Wide = ZoomWide,
Tele = ZoomTele
}
public enum Switch
{
On,
Off
}
public enum Focus
{
Near = FocusNear,
Far = FocusFar
}
public enum PresetAction
{
Set = 0x03,
Clear = 0x05,
Goto = 0x07
}
#endregion Enums
#region Extended Command Set
public static byte[] Preset(uint deviceAddress, PresetAction action, byte presetId)
{
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, presetId);
}
public static byte[] Flip(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x07, 0x00, 0x21);
}
public static byte[] ZeroPanPosition(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x07, 0x00, 0x22);
}
public static byte[] AutoScan(uint deviceAddress, Action action)
{
byte m_action;
if (action == Action.Start)
m_action = 0x09;
else
m_action = 0x0B;
return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
}
public static byte[] RemoteReset(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x0F, 0x00, 0x00);
}
public static byte[] Zone(uint deviceAddress, byte zone, Action action)
{
if (zone < 0x01 & zone > 0x08)
throw new Exception("Zone value should be between 0x01 and 0x08 include");
byte m_action;
if (action == Action.Start)
m_action = 0x11;
else
m_action = 0x13;
return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, zone);
}
public static byte[] WriteToScreen(uint deviceAddress, string text)
{
if (text.Length > 40)
text = text.Remove(40, text.Length - 40);
System.Text.Encoding encoding = System.Text.Encoding.ASCII;
byte[] m_bytes = new byte[encoding.GetByteCount(text) * 8];
int i = 0;
byte m_scrPosition;
byte m_ASCIIchr;
foreach (char ch in text)
{
m_scrPosition = Convert.ToByte(i / 8);
m_ASCIIchr = Convert.ToByte(ch);
Array.Copy(
Message.GetMessage(deviceAddress, 0x00, 0x15, m_scrPosition, m_ASCIIchr),
0,
m_bytes,
i,
8
);
i += 8;
}
return m_bytes;
}
public static byte[] ClearScreen(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x17, 0x00, 0x00);
}
public static byte[] AlarmAcknowledge(uint deviceAddress, uint alarmID)
{
if (alarmID < 1 & alarmID > 8)
throw new Exception("Only 8 alarms allowed for Pelco P implementation");
return Message.GetMessage(deviceAddress, 0x00, 0x19, 0x00, Convert.ToByte(alarmID));
}
public static byte[] ZoneScan(uint deviceAddress, Action action)
{
byte m_action;
if (action == Action.Start)
m_action = 0x1B;
else
m_action = 0x1D;
return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
}
public static byte[] Pattern(uint deviceAddress, PatternAction action)
{
byte m_action = action switch
{
PatternAction.Start => 0x1F,
PatternAction.Stop => 0x21,
PatternAction.Run => 0x23,
_ => 0x23,
};
return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
}
public static byte[] SetZoomLensSpeed(uint deviceAddress, LensSpeed speed)
{
return Message.GetMessage(deviceAddress, 0x00, 0x25, 0x00, (byte)speed);
}
public static byte[] SetFocusLensSpeed(uint deviceAddress, LensSpeed speed)
{
return Message.GetMessage(deviceAddress, 0x00, 0x27, 0x00, (byte)speed);
}
#endregion Extended Command Set
#region Base Command Set
public static byte[] CameraSwitch(uint deviceAddress, Switch action)
{
byte m_action = CameraOnOff;
if (action == Switch.On)
m_action += CameraOnOff; //Maybe wrong !!!
return Message.GetMessage(deviceAddress, m_action, 0x00, 0x00, 0x00);
}
public static byte[] CameraIrisSwitch(uint deviceAddress, Iris action)
{
return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
}
public static byte[] CameraFocus(uint deviceAddress, Focus action)
{
return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
}
public static byte[] CameraZoom(uint deviceAddress, Zoom action)
{
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
}
public static byte[] CameraTilt(uint deviceAddress, Tilt action, uint speed)
{
if (speed < TiltSpeedMin)
speed = TiltSpeedMin;
if (speed > TiltSpeedMax)
speed = TiltSpeedMax;
return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, (byte)speed);
}
public static byte[] CameraPan(uint deviceAddress, Pan action, uint speed)
{
if (speed < PanSpeedMin)
speed = PanSpeedMin;
if (speed > PanSpeedMax)
speed = PanSpeedMax;
return Message.GetMessage(deviceAddress, 0x00, (byte)action, (byte)speed, 0x00);
}
public static byte[] CameraPanTilt(
uint deviceAddress,
Pan panAction,
uint panSpeed,
Tilt tiltAction,
uint tiltSpeed
)
{
byte[] m_tiltMessage = CameraTilt(deviceAddress, tiltAction, tiltSpeed);
byte[] m_panMessage = CameraPan(deviceAddress, panAction, panSpeed);
byte[] m_bytes = Message.GetMessage(
deviceAddress,
0x00,
(byte)(m_tiltMessage[3] + m_panMessage[3]),
m_panMessage[4],
m_tiltMessage[5]
);
return m_bytes;
}
public static byte[] CameraStop(uint deviceAddress)
{
return Message.GetMessage(deviceAddress, 0x00, 0x00, 0x00, 0x00);
}
#endregion Base Command Set
public struct Message
{
public static byte Address;
public static byte CheckSum;
public static byte Data1,
Data2,
Data3,
Data4;
public static byte[] GetMessage(
uint address,
byte data1,
byte data2,
byte data3,
byte data4
)
{
if (address < 0 & address > 32)
throw new Exception("Protocol Pelco P support 32 devices only");
Address = byte.Parse((address - 1).ToString());
Data1 = data1;
Data2 = data2;
Data3 = data3;
Data4 = data4;
CheckSum = (byte)(STX ^ Address ^ Data1 ^ Data2 ^ Data3 ^ Data4 ^ ETX);
return new byte[] { STX, Address, Data1, Data2, Data3, Data4, ETX, CheckSum };
}
}
}

415
JiLinApp.Docking/Ptz/PtzCmd.cs

@ -0,0 +1,415 @@
using EC.Util.CameraSDK;
namespace JiLinApp.Docking.Ptz;
public class PtzCmd
{
public static PtzControlType GetControlType(string ctrlType)
{
return ctrlType switch
{
"PelcoD" => PtzControlType.PelcoD,
"PelcoP" => PtzControlType.PelcoP,
"DCamera" => PtzControlType.DCamera,
"CameraSdk" => PtzControlType.CameraSdk,
_ => PtzControlType.None
};
}
public static PtzCmdType GetCmdType(string cmdStr)
{
return cmdStr switch
{
"Left" => PtzCmdType.Left,
"Right" => PtzCmdType.Right,
"Top" => PtzCmdType.Top,
"Down" => PtzCmdType.Down,
"LeftTop" => PtzCmdType.LeftTop,
"LeftDown" => PtzCmdType.LeftDown,
"RightTop" => PtzCmdType.RightTop,
"RightDown" => PtzCmdType.RightDown,
"Stop" => PtzCmdType.Stop,
"AutoMove" => PtzCmdType.AutoMove,
"ZoomIn" => PtzCmdType.ZoomIn,
"ZoomOut" => PtzCmdType.ZoomOut,
"FocusNear" => PtzCmdType.FocusNear,
"FocusFar" => PtzCmdType.FocusFar,
"IrisOpen" => PtzCmdType.IrisOpen,
"IrisClose" => PtzCmdType.IrisClose,
"PresetSet" => PtzCmdType.PresetSet,
"PresetClear" => PtzCmdType.PresetClear,
"PresetGoto" => PtzCmdType.PresetGoto,
_ => PtzCmdType.None
};
}
}
public class PtzComCmd
{
/// <summary>
/// GetPelcoDCmd
/// GetPelcoPCmd
/// DCamearCmd
/// </summary>
/// <param name="ctrlType"></param>
/// <param name="cmdType"></param>
/// <param name="args"></param>
/// <returns></returns>
public static byte[] GetCmd(PtzControlType ctrlType, PtzCmdType cmdType, object[] args = null)
{
byte[] cmd = ctrlType switch
{
PtzControlType.PelcoD => GetPelcoDCmd(cmdType, args),
PtzControlType.PelcoP => GetPelcoPCmd(cmdType, args),
PtzControlType.DCamera => GetDCameraCmd(cmdType, args),
_ => Array.Empty<byte>(),
};
return cmd;
}
public static byte[] GetPelcoDCmd(PtzCmdType cmdType, object[] args)
{
uint addr = (byte)args[0];
uint panSpeed = 0x2f;
uint tiltSpeed = (uint)(PelcoD.TiltSpeedMax + PelcoD.TiltSpeedMin) / 2;
byte[] cmd = cmdType switch
{
PtzCmdType.Left => PelcoD.CameraPan(addr, PelcoD.Pan.Left, panSpeed),
PtzCmdType.Right => PelcoD.CameraPan(addr, PelcoD.Pan.Right, panSpeed),
PtzCmdType.Top => PelcoD.CameraTilt(addr, PelcoD.Tilt.Up, tiltSpeed),
PtzCmdType.Down => PelcoD.CameraTilt(addr, PelcoD.Tilt.Down, tiltSpeed),
PtzCmdType.LeftTop => PelcoD.CameraPan(addr, PelcoD.Pan.LeftUp, panSpeed),
PtzCmdType.LeftDown => PelcoD.CameraPan(addr, PelcoD.Pan.LeftDown, panSpeed),
PtzCmdType.RightTop => PelcoD.CameraPan(addr, PelcoD.Pan.RightUp, panSpeed),
PtzCmdType.RightDown => PelcoD.CameraPan(addr, PelcoD.Pan.RightDown, panSpeed),
PtzCmdType.Stop => PelcoD.CameraStop(addr),
PtzCmdType.AutoMove => PelcoD.CameraScan(addr, PelcoD.Scan.Auto),
//PtzCmdType.AutoStop => PelcoD.CameraScan(addr, PelcoD.Scan.Manual),
PtzCmdType.PresetGoto => PelcoD.Preset(addr, PelcoD.PresetAction.Goto, (byte)args[1]),
_ => Array.Empty<byte>(),
};
return cmd;
}
public static byte[] GetPelcoPCmd(PtzCmdType cmdType, object[] args)
{
uint addr = (byte)args[0];
uint panSpeed = (uint)(PelcoP.PanSpeedMax + PelcoP.PanSpeedMin) / 2;
uint tiltSpeed = (uint)(PelcoP.TiltSpeedMax + PelcoP.TiltSpeedMin) / 2;
byte[] cmd = cmdType switch
{
PtzCmdType.Left => PelcoP.CameraPan(addr, PelcoP.Pan.Left, panSpeed),
PtzCmdType.Right => PelcoP.CameraPan(addr, PelcoP.Pan.Right, panSpeed),
PtzCmdType.Top => PelcoP.CameraTilt(addr, PelcoP.Tilt.Up, tiltSpeed),
PtzCmdType.Down => PelcoP.CameraTilt(addr, PelcoP.Tilt.Down, tiltSpeed),
PtzCmdType.LeftTop => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Left, panSpeed, PelcoP.Tilt.Up, tiltSpeed),
PtzCmdType.LeftDown => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Left, panSpeed, PelcoP.Tilt.Down, tiltSpeed),
PtzCmdType.RightTop => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Right, panSpeed, PelcoP.Tilt.Up, tiltSpeed),
PtzCmdType.RightDown => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Right, panSpeed, PelcoP.Tilt.Down, tiltSpeed),
PtzCmdType.Stop => PelcoP.CameraStop(addr),
PtzCmdType.AutoMove => PelcoP.AutoScan(addr, PelcoP.Action.Start),
//PtzCmdType.AutoStop => PelcoP.AutoScan(addr, PelcoP.Action.Stop),
PtzCmdType.PresetGoto => PelcoP.Preset(addr, PelcoP.PresetAction.Goto, (byte)args[1]),
_ => Array.Empty<byte>(),
};
return cmd;
}
public static byte[] GetDCameraCmd(PtzCmdType cmdType, object[] args)
{
double speed = (DCamera.SpeedMax + DCamera.SpeedMin) / 2;
byte[] cmd = cmdType switch
{
PtzCmdType.Left => DCamera.Left(speed),
PtzCmdType.Right => DCamera.Right(speed),
PtzCmdType.Top => DCamera.Up(speed),
PtzCmdType.Down => DCamera.Down(speed),
PtzCmdType.LeftTop => DCamera.LeftUp(speed, speed),
PtzCmdType.LeftDown => DCamera.LeftDown(speed, speed),
PtzCmdType.RightTop => DCamera.RightUp(speed, speed),
PtzCmdType.RightDown => DCamera.RightDown(speed, speed),
PtzCmdType.Stop => DCamera.Stop(),
_ => Array.Empty<byte>(),
};
return cmd;
}
}
public class PtzCameraCmd
{
public static void PtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
if (sdk == null || !sdk.ConnectSuccess() || args == null) return;
switch (sdk.CameraInfo.Manufactor)
{
case CameraManufactor.HiK:
HikPtzMove(sdk, cmdType, args);
break;
case CameraManufactor.DaHua:
DaHuaPtzMove(sdk, cmdType, args);
break;
case CameraManufactor.YuShi:
YuShiPtzMove(sdk, cmdType, args);
break;
default:
break;
}
}
public static void HikPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (HiKOriSdk.PtzSpeedMin + HiKOriSdk.PtzSpeedMax) / 2;
switch (cmdType)
{
case PtzCmdType.Left:
sdk.PtzMove(HiKOriSdk.PAN_LEFT, stop, speed);
break;
case PtzCmdType.Right:
sdk.PtzMove(HiKOriSdk.PAN_RIGHT, stop, speed);
break;
case PtzCmdType.Top:
sdk.PtzMove(HiKOriSdk.TILT_UP, stop, speed);
break;
case PtzCmdType.Down:
sdk.PtzMove(HiKOriSdk.TILT_DOWN, stop, speed);
break;
case PtzCmdType.LeftTop:
sdk.PtzMove(HiKOriSdk.UP_LEFT, stop, speed);
break;
case PtzCmdType.LeftDown:
sdk.PtzMove(HiKOriSdk.DOWN_LEFT, stop, speed);
break;
case PtzCmdType.RightTop:
sdk.PtzMove(HiKOriSdk.UP_RIGHT, stop, speed);
break;
case PtzCmdType.RightDown:
sdk.PtzMove(HiKOriSdk.DOWN_RIGHT, stop, speed);
break;
case PtzCmdType.AutoMove:
sdk.PtzMove(HiKOriSdk.PAN_AUTO, stop, speed);
break;
case PtzCmdType.ZoomIn:
sdk.PtzMove(HiKOriSdk.ZOOM_IN, stop, speed);
break;
case PtzCmdType.ZoomOut:
sdk.PtzMove(HiKOriSdk.ZOOM_OUT, stop, speed);
break;
case PtzCmdType.FocusNear:
sdk.PtzMove(HiKOriSdk.FOCUS_NEAR, stop, speed);
break;
case PtzCmdType.FocusFar:
sdk.PtzMove(HiKOriSdk.FOCUS_FAR, stop, speed);
break;
case PtzCmdType.IrisOpen:
sdk.PtzMove(HiKOriSdk.IRIS_OPEN, stop, speed);
break;
case PtzCmdType.IrisClose:
sdk.PtzMove(HiKOriSdk.IRIS_CLOSE, stop, speed);
break;
case PtzCmdType.PresetGoto:
sdk.PtzPreset(HiKOriSdk.GOTO_PRESET, presetId);
break;
default:
break;
}
}
private static void DaHuaPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (DaHuaOriSdk.PtzSpeedMin + DaHuaOriSdk.PtzSpeedMax) / 2;
switch (cmdType)
{
case PtzCmdType.Left:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.LEFT, stop, speed);
break;
case PtzCmdType.Right:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.RIGHT, stop, speed);
break;
case PtzCmdType.Top:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.UP, stop, speed);
break;
case PtzCmdType.Down:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.DOWN, stop, speed);
break;
case PtzCmdType.LeftTop:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.LEFTTOP, stop, speed);
break;
case PtzCmdType.LeftDown:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.LEFTDOWN, stop, speed);
break;
case PtzCmdType.RightTop:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.RIGHTTOP, stop, speed);
break;
case PtzCmdType.RightDown:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.RIGHTDOWN, stop, speed);
break;
case PtzCmdType.AutoMove:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.STARTPANCRUISE, stop, speed);
break;
case PtzCmdType.ZoomIn:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.ZOOM_ADD, stop, speed);
break;
case PtzCmdType.ZoomOut:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.ZOOM_DEC, stop, speed);
break;
case PtzCmdType.FocusNear:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.FOCUS_ADD, stop, speed);
break;
case PtzCmdType.FocusFar:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.FOCUS_DEC, stop, speed);
break;
case PtzCmdType.IrisOpen:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.APERTURE_ADD, stop, speed);
break;
case PtzCmdType.IrisClose:
sdk.PtzMove((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.APERTURE_DEC, stop, speed);
break;
case PtzCmdType.PresetGoto:
sdk.PtzPreset((int)DaHuaOriSdk.EM_EXTPTZ_ControlType.GOTOPRESET, presetId);
break;
default:
break;
}
}
public static void YuShiPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (YuShiOriSdk.PtzSpeedMax + YuShiOriSdk.PtzSpeedMin) / 2;
switch (cmdType)
{
case PtzCmdType.Left:
sdk.PtzMove(YuShiOriSdk.PANLEFT, stop, speed);
break;
case PtzCmdType.Right:
sdk.PtzMove(YuShiOriSdk.PANRIGHT, stop, speed);
break;
case PtzCmdType.Top:
sdk.PtzMove(YuShiOriSdk.TILTUP, stop, speed);
break;
case PtzCmdType.Down:
sdk.PtzMove(YuShiOriSdk.TILTDOWN, stop, speed);
break;
case PtzCmdType.LeftTop:
sdk.PtzMove(YuShiOriSdk.LEFTUP, stop, speed);
break;
case PtzCmdType.LeftDown:
sdk.PtzMove(YuShiOriSdk.LEFTDOWN, stop, speed);
break;
case PtzCmdType.RightTop:
sdk.PtzMove(YuShiOriSdk.RIGHTUP, stop, speed);
break;
case PtzCmdType.RightDown:
sdk.PtzMove(YuShiOriSdk.RIGHTDOWN, stop, speed);
break;
case PtzCmdType.ZoomIn:
sdk.PtzMove(YuShiOriSdk.ZOOMTELE, stop, speed);
break;
case PtzCmdType.ZoomOut:
sdk.PtzMove(YuShiOriSdk.ZOOMWIDE, stop, speed);
break;
case PtzCmdType.FocusNear:
sdk.PtzMove(YuShiOriSdk.FOCUSNEAR, stop, speed);
break;
case PtzCmdType.FocusFar:
sdk.PtzMove(YuShiOriSdk.FOCUSFAR, stop, speed);
break;
case PtzCmdType.IrisOpen:
sdk.PtzMove(YuShiOriSdk.IRISOPEN, stop, speed);
break;
case PtzCmdType.IrisClose:
sdk.PtzMove(YuShiOriSdk.IRISCLOSE, stop, speed);
break;
case PtzCmdType.PresetGoto:
sdk.PtzPreset(YuShiOriSdk.PRESET_GOTO, presetId);
break;
default:
break;
}
}
}
public enum PtzControlType
{
PelcoD,
PelcoP,
DCamera,
CameraSdk,
None,
}
public enum PtzCmdType
{
Left,
Right,
Top,
Down,
LeftTop,
LeftDown,
RightTop,
RightDown,
Stop,
AutoMove,
ZoomIn,
ZoomOut,
FocusNear,
FocusFar,
IrisOpen,
IrisClose,
PresetSet,
PresetClear,
PresetGoto,
None
}

38
JiLinApp.Docking/Ptz/PtzConfig.cs

@ -0,0 +1,38 @@
namespace JiLinApp.Docking.Ptz;
public class PtzControlTypeConfig
{
public string Type { get; set; }
public string Name { get; set; }
}
public class PtzControlTypeConfigHelper
{
private static Dictionary<string, PtzControlTypeConfig> PctConfigDict { get; set; }
public static void Init(List<PtzControlTypeConfig> ptzCtrlTypes)
{
if (PctConfigDict != null) return;
Dictionary<string, PtzControlTypeConfig> dict = new();
foreach (var cfg in ptzCtrlTypes)
{
dict.Add(cfg.Name, cfg);
}
PctConfigDict ??= dict;
}
public static PtzControlType GetControlType(string ctrlName)
{
PctConfigDict.TryGetValue(ctrlName, out var cfg);
string ctrlType = cfg != null ? cfg.Type : "";
return ctrlType switch
{
"PelcoD" => PtzControlType.PelcoD,
"PelcoP" => PtzControlType.PelcoP,
"DCamera" => PtzControlType.DCamera,
"CameraSdk" => PtzControlType.CameraSdk,
_ => PtzControlType.None
};
}
}

130
JiLinApp.Docking/VibrateAlarm/Entity/ClientMessage.cs

@ -0,0 +1,130 @@
using EC.Util.Common;
using System.Collections.Concurrent;
using System.Net.Sockets;
namespace JiLinApp.Docking.VibrateAlarm;
public class ClientMessage
{
#region Fields
public TcpClient Client { get; set; }
public TcpAlarmHost Host { get; set; }
public int OnlineState { get; set; }//设备在线状态
public bool IsOnline
{
get
{
return Host != null && Host.Id >= 0 && OnlineState == 1;
}
}
public ConcurrentDictionary<int, SensorState> SensorDict { get; } = new();
public int SensorTotal
{
get
{
return SensorDict.Count;
}
}
public bool SensorsEmpty
{
get
{
return SensorDict == null || SensorDict.IsEmpty;
}
}
public List<byte> DataList { get; } = new();
public string ClientAddr
{
get
{
return Client.ClientAddr();
}
}
public string ClientIp
{
get
{
if (Host != null) return Host.Ip;
return Client.ClientIp();
}
}
public string ClientPort
{
get
{
if (Host != null) return Host.Port;
return Client.ClientPort();
}
}
public string LocalAddr
{
get
{
return Client.LocalAddr();
}
}
public string LocalIp
{
get
{
return Client.LocalIp();
}
}
public string LocalPort
{
get
{
return Client.LocalPort();
}
}
#endregion Fields
public ClientMessage()
{
}
public void AddData(byte[] bytes)
{
DataList.AddRange(bytes);
}
public List<byte[]> GetMessageList()
{
List<byte[]> msglist = new();
while (DataList.Count >= 19)
{
if (DataList[0] == 0xAA && DataList[1] == 0xAA)
{
int num = DataList[17];
if (DataList.Count < 19 + num) break;
byte[] bytes = new byte[num + 19];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = DataList[i];
}
msglist.Add(bytes);
DataList.RemoveRange(0, num + 19);
}
else
{
DataList.RemoveAt(0);
}
}
return msglist;
}
}

80
JiLinApp.Docking/VibrateAlarm/Entity/DataMessage.cs

@ -0,0 +1,80 @@
using System.Text;
namespace JiLinApp.Docking.VibrateAlarm;
public class DataMessage
{
private byte HeadA = 0xAA;
private byte HeadB = 0xAA; //(1)帧头
public int DeviceId { get; set; } //(2)设备ID
public string SendIp { get; set; } //(3)原地址
public int SendPort { get; set; }//(3)原地址
public string ReceiveIp { get; set; }//(4)目标地址
public int ReceivePort { get; set; }//(4)目标地址
public byte FrameNum { get; set; }//(5)帧编号
public byte FunctionNum { get; set; }//(6)功能码
public byte DataLen { get; set; }//(7)传输数据长度
public byte[] Data { get; set; }//(8)传输数据
public byte CRC { get; set; }//(9)CRC8
public void Decode(byte[] bytes)
{
DeviceId = bytes[2];
SendIp = bytes[3].ToString() + "." + bytes[4].ToString() + "." + bytes[5].ToString() + "." + bytes[6].ToString();
SendPort = bytes[8] * 256 + bytes[7];
ReceiveIp = bytes[9].ToString() + "." + bytes[10].ToString() + "." + bytes[11].ToString() + "." + bytes[12].ToString();
ReceivePort = bytes[14] * 256 + bytes[13];
FrameNum = bytes[15];
FunctionNum = bytes[16];
DataLen = bytes[17];
Data = new byte[DataLen];
for (int i = 0; i < DataLen; i++)
{
Data[i] = bytes[18 + i];
}
CRC = bytes[18 + DataLen];
}
public byte[] Encode()
{
byte[] bytes = new byte[19 + DataLen];
bytes[0] = bytes[1] = 0xAA;
bytes[2] = (byte)DeviceId;
string[] strs = SendIp.Split('.');
bytes[3] = byte.Parse(strs[0]);
bytes[4] = byte.Parse(strs[1]);
bytes[5] = byte.Parse(strs[2]);
bytes[6] = byte.Parse(strs[3]);
bytes[7] = (byte)(SendPort % 256);
bytes[8] = (byte)(SendPort / 256);
string[] strs2 = ReceiveIp.Split('.');
bytes[9] = byte.Parse(strs2[0]);
bytes[10] = byte.Parse(strs2[1]);
bytes[11] = byte.Parse(strs2[2]);
bytes[12] = byte.Parse(strs2[3]);
bytes[13] = (byte)(ReceivePort % 256);
bytes[14] = (byte)(ReceivePort / 256);
bytes[15] = FrameNum;
bytes[16] = FunctionNum;
bytes[17] = DataLen;
for (int i = 0; i < DataLen; i++)
{
bytes[18 + i] = Data[i];
}
bytes[bytes.Length - 1] = CRC8.CRC(bytes, 0, bytes.Length - 1);
return bytes;
}
public static string ToHexString(byte[] bytes)
{
if (bytes == null) return string.Empty;
StringBuilder builder = new();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("X2") + " ");
}
return builder.ToString();
}
}

12
JiLinApp.Docking/VibrateAlarm/Entity/DataRequest.cs

@ -0,0 +1,12 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class DataRequest
{
public DataMessage Request { get; set; }
public DataMessage Responce { get; set; }
public DataRequest()
{
}
}

68
JiLinApp.Docking/VibrateAlarm/Entity/SensorState.cs

@ -0,0 +1,68 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class SensorState
{
public int DeviceId { get; set; }
public int Addr { get; set; }
public int OfflineState { get; set; }
public int OnlineState
{
get
{
if (OfflineState == 0) return 1;
else if (OfflineState == 1) return 0;
else return 0;
}
}
public string OnlineStateStr
{
get
{
if (OfflineState == 0)
{
return "在线";
}
else
{
return "离线";
}
}
}
public int AlarmState { get; set; }
public string AlarmStateStr
{
get
{
if (AlarmState == 0)
{
return "消警";
}
else
{
return "报警";
}
}
}
public SensorState(int deviceId, int sensorAddr, int state)
{
DeviceId = deviceId;
Addr = sensorAddr;
OfflineState = state % 2;
AlarmState = state / 2 % 2;
}
public SensorState(int deviceId, int sensorAddr, int offlineState, int alarmState)
{
DeviceId = deviceId;
Addr = sensorAddr;
OfflineState = offlineState;
AlarmState = alarmState;
}
}

22
JiLinApp.Docking/VibrateAlarm/Entity/TcpAlarmHost.cs

@ -0,0 +1,22 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpAlarmHost
{
public int Id { get; set; }
public string Name
{
get
{
return "设备" + Id;
}
}
public string Ip { get; set; }
public string Port { get; set; }
public string Lat { get; set; }
public string Lng { get; set; }
}

19
JiLinApp.Docking/VibrateAlarm/Entity/TcpAlarmHostMessage.cs

@ -0,0 +1,19 @@
using Newtonsoft.Json.Linq;
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpAlarmHostMessage
{
public string AlarmTime { get; set; }//报警时间
public string CID { get; set; }//CID代码
/*设备信息*/
public int DeviceID { get; set; }//设备唯一ID
public string SensorAddr { get; set; }//防区号
public string Mode { get; set; }
public string Sensitivity { get; set; }
/*联动信息*/
public bool IsLinked { get; set; }//是否有联动信息
public JArray Linklist { get; set; }
}

10
JiLinApp.Docking/VibrateAlarm/Entity/TcpManagerConfig.cs

@ -0,0 +1,10 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpManagerConfig
{
public string ServerIp { get; set; }
public int ServerPort { get; set; }
public int DeviceHeartKeep { get; set; }
}

20
JiLinApp.Docking/VibrateAlarm/Entity/TcpSensorTable.cs

@ -0,0 +1,20 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpSensorTable
{
public int Id { get; set; }
public int DeviceId { get; set; }
public string Name { get; set; }
public string Lat { get; set; }
public string Lng { get; set; }
public string Channel { get; set; }
public string Mode { get; set; }
public string Sensitivity { get; set; }
}

436
JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs

@ -0,0 +1,436 @@
using EC.Util.Common;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace JiLinApp.Docking.VibrateAlarm;
public class AsyncTcpServer : IDisposable
{
#region Fields
private TcpListener Listener { get; set; }
private ConcurrentDictionary<string, TcpClientState> Clients { get; }
#region Properties
/// <summary>
/// 监听的IP地址
/// </summary>
public IPAddress Address { get; private set; }
/// <summary>
/// 监听的端口
/// </summary>
public int Port { get; private set; }
/// <summary>
/// 通信使用的编码
/// </summary>
public Encoding Encoding { get; set; }
public bool Disposed { get; private set; }
#endregion Properties
#endregion Fields
#region Ctors
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="port">监听的端口</param>
public AsyncTcpServer(int port) : this(IPAddress.Any, port)
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="ipep">监听的终结点</param>
public AsyncTcpServer(IPEndPoint ipep) : this(ipep.Address, ipep.Port)
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="address">监听的IP地址</param>
/// <param name="port">监听的端口</param>
public AsyncTcpServer(IPAddress address, int port)
{
Address = address;
Port = port;
Encoding = Encoding.Default;
Listener = new(address, port);
Clients = new();
Listener.AllowNatTraversal(true);
}
~AsyncTcpServer()
{
Stop();
}
#endregion Ctors
#region Server
/// <summary>
/// 启动服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public void Start()
{
Start(30);
}
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="backlog">服务器所允许的挂起连接序列的最大长度</param>
/// <returns>异步TCP服务器</returns>
public void Start(int backlog)
{
if (IsRunning()) return;
Listener.Start(backlog);
AcceptTcpClient(Listener);
}
/// <summary>
/// 停止服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public void Stop()
{
if (!IsRunning()) return;
try
{
Listener.Stop();
}
finally
{
foreach (var client in Clients.Values)
{
client.Client.Client.Disconnect(false);
}
Clients.Clear();
}
}
public bool IsRunning()
{
return Listener != null && Listener.Server.IsBound;
}
public ICollection<TcpClientState> GetAllClient()
{
return Clients.Values;
}
#endregion Server
#region Events
/// <summary>
/// 与客户端的连接已建立事件
/// </summary>
public event EventHandler<TcpClientConnectedEventArgs>? ClientConnected;
/// <summary>
/// 与客户端的连接已断开事件
/// </summary>
public event EventHandler<TcpClientDisconnectedEventArgs>? ClientDisconnected;
/// <summary>
/// 接收到数据报文事件
/// </summary>
public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>>? DatagramReceived;
private void RaiseClientConnected(string clientAddr, TcpClientState clientState)
{
if (string.IsNullOrEmpty(clientAddr) || clientAddr.Equals(":")) return;
Clients.AddOrUpdate(clientAddr, clientState, (n, o) => { return clientState; });
ClientConnected?.Invoke(this, new TcpClientConnectedEventArgs(clientState.Client));
}
private void RaiseClientDisconnected(string clientAddr, TcpClient client)
{
if (string.IsNullOrEmpty(clientAddr) || clientAddr.Equals(":")) return;
client.Client.Disconnect(false);
if (Clients.TryRemove(clientAddr, out _))
{
ClientDisconnected?.Invoke(this, new TcpClientDisconnectedEventArgs(client));
}
}
private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram)
{
DatagramReceived?.Invoke(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
}
#endregion Events
#region Receive
private void AcceptTcpClient(TcpListener listener)
{
listener.BeginAcceptTcpClient(HandleTcpClientAccepted, listener);
}
private void HandleTcpClientAccepted(IAsyncResult ar)
{
if (!IsRunning()) return;
if (ar.AsyncState is not TcpListener listener) return;
TcpClient? client;
try
{
client = listener.EndAcceptTcpClient(ar);
if (client == null || !client.Connected)
{
client?.Client.Disconnect(false);
return;
}
}
catch (Exception)
{
return;
}
// add client connection to cache
string clientAddr = client.ClientAddr();
byte[] buffer = new byte[client.ReceiveBufferSize];
TcpClientState clientState = new(client, buffer);
RaiseClientConnected(clientAddr, clientState);
// begin to read data
ReadBuffer(clientState);
// keep listening to accept next connection
AcceptTcpClient(listener);
}
private void ReadBuffer(TcpClientState clientState)
{
try
{
NetworkStream stream = clientState.GetStream;
if (clientState.IsRead) return;
lock (clientState.IsReadLock)
{
if (clientState.IsRead) return;
clientState.IsRead = true;
stream.BeginRead(clientState.Buffer, 0, clientState.Buffer.Length, HandleDatagramReceived, clientState);
}
}
catch (IOException e)
{
LogUnit.Error(e);
return;
}
catch (Exception e)
{
LogUnit.Error(e);
string clientAddr = clientState.Client.ClientAddr();
RaiseClientDisconnected(clientAddr, clientState.Client);
}
}
private void HandleDatagramReceived(IAsyncResult ar)
{
if (!IsRunning()) return;
if (ar.AsyncState is not TcpClientState clientState) return;
int readNum;
string clientAddr = clientState.Client.ClientAddr();
try
{
NetworkStream networkStream = clientState.GetStream;
// if the remote host has shutdown its connection, read will immediately return with zero bytes.
readNum = networkStream.EndRead(ar);
if (readNum == 0)
{
RaiseClientDisconnected(clientAddr, clientState.Client);
return;
}
}
catch (Exception)
{
RaiseClientDisconnected(clientAddr, clientState.Client);
return;
}
// received byte and trigger event notification
byte[] receivedBytes = new byte[readNum];
Buffer.BlockCopy(clientState.Buffer, 0, receivedBytes, 0, readNum);
RaiseDatagramReceived(clientState, receivedBytes);
lock (clientState.IsReadLock)
{
clientState.IsRead = false;
}
// continue listening for tcp datagram packets
ReadBuffer(clientState);
}
#endregion Receive
#region Send
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="client">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient client, byte[] datagram)
{
if (!IsRunning()) return;
if (client == null || datagram == null) return;
try
{
NetworkStream stream = client.GetStream();
stream.Write(datagram, 0, datagram.Length);
}
catch (Exception)
{
string clientAddr = client.ClientAddr();
RaiseClientDisconnected(clientAddr, client);
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="client">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient client, string datagram)
{
Send(client, Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(byte[] datagram)
{
if (!IsRunning()) return;
foreach (var client in Clients.Values)
{
Send(client.Client, datagram);
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(string datagram)
{
if (!IsRunning()) return;
SendToAll(Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="client">客户端</param>
/// <param name="datagram">报文</param>
public bool SendAsync(TcpClient client, byte[] datagram)
{
if (!IsRunning()) return false;
if (client == null || datagram == null) return false;
try
{
NetworkStream stream = client.GetStream();
IAsyncResult result = stream.BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, client);
return result.IsCompleted;
}
catch (Exception)
{
string clientAddr = client.ClientAddr();
RaiseClientDisconnected(clientAddr, client);
}
return false;
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="client">客户端</param>
/// <param name="datagram">报文</param>
public bool SendAsync(TcpClient client, string datagram)
{
return SendAsync(client, Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAllAsync(byte[] datagram)
{
if (!IsRunning()) return;
foreach (var client in Clients.Values)
{
SendAsync(client.Client, datagram);
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAllAsync(string datagram)
{
if (!IsRunning()) return;
SendToAllAsync(Encoding.GetBytes(datagram));
}
private void HandleDatagramWritten(IAsyncResult ar)
{
if (ar.AsyncState is not TcpClient client) return;
try
{
client.GetStream().EndWrite(ar);
}
catch (Exception)
{
string clientAddr = client.ClientAddr();
RaiseClientDisconnected(clientAddr, client);
}
}
#endregion Send
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!Disposed)
{
if (disposing) Stop();
Disposed = true;
}
}
#endregion IDisposable Members
}

49
JiLinApp.Docking/VibrateAlarm/Service/CRC8.cs

@ -0,0 +1,49 @@
namespace JiLinApp.Docking.VibrateAlarm;
public class CRC8
{
/// <summary>
/// CRC8位校验表
/// </summary>
private static byte[] CRC8Table = new byte[] {
0,94,188,226,97,63,221,131,194,156,126,32,163,253,31,65,
157,195,33,127,252,162,64,30, 95,1,227,189,62,96,130,220,
35,125,159,193,66,28,254,160,225,191,93,3,128,222,60,98,
190,224,2,92,223,129,99,61,124,34,192,158,29,67,161,255,
70,24,250,164,39,121,155,197,132,218,56,102,229,187,89,7,
219,133,103,57,186,228,6,88,25,71,165,251,120,38,196,154,
101,59,217,135,4,90,184,230,167,249,27,69,198,152,122,36,
248,166,68,26,153,199,37,123,58,100,134,216,91,5,231,185,
140,210,48,110,237,179,81,15,78,16,242,172,47,113,147,205,
17,79,173,243,112,46,204,146,211,141,111,49,178,236,14,80,
175,241,19,77,206,144,114,44,109,51,209,143,12,82,176,238,
50,108,142,208,83,13,239,177,240,174,76,18,145,207,45,115,
202,148,118,40,171,245,23,73,8,86,180,234,105,55,213,139,
87,9,235,181,54,104,138,212,149,203, 41,119,244,170,72,22,
233,183,85,11,136,214,52,106,43,117,151,201,74,20,246,168,
116,42,200,150,21,75,169,247,182,232,10,84,215,137,107,53 };
public static byte CRC(byte[] buffer)
{
return CRC(buffer, 0, buffer.Length);
}
public static byte CRC(byte[] buffer, int off, int len)
{
byte crc = 0;
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (off < 0 || len < 0 || off + len > buffer.Length)
{
throw new ArgumentOutOfRangeException();
}
for (int i = off; i < len; i++)
{
crc = CRC8Table[crc ^ buffer[i]];
}
return crc;
}
}

23
JiLinApp.Docking/VibrateAlarm/Service/TcpClientConnectedEventArgs.cs

@ -0,0 +1,23 @@
using System.Net.Sockets;
namespace JiLinApp.Docking.VibrateAlarm;
/// <summary>
/// 与客户端的连接已建立事件参数
/// </summary>
public class TcpClientConnectedEventArgs : EventArgs
{
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
/// <summary>
/// 与客户端的连接已建立事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
public TcpClientConnectedEventArgs(TcpClient tcpClient)
{
TcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
}
}

23
JiLinApp.Docking/VibrateAlarm/Service/TcpClientDisconnectedEventArgs.cs

@ -0,0 +1,23 @@
using System.Net.Sockets;
namespace JiLinApp.Docking.VibrateAlarm;
/// <summary>
/// 与客户端的连接已断开事件参数
/// </summary>
public class TcpClientDisconnectedEventArgs : EventArgs
{
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
/// <summary>
/// 与客户端的连接已断开事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
public TcpClientDisconnectedEventArgs(TcpClient tcpClient)
{
TcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
}
}

53
JiLinApp.Docking/VibrateAlarm/Service/TcpClientState.cs

@ -0,0 +1,53 @@
using System.Net.Sockets;
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpClientState
{
/// <summary>
/// Constructor for a new Client
/// </summary>
/// <param name="client">The TCP client</param>
/// <param name="buffer">The byte array buffer</param>
public TcpClientState(TcpClient client, byte[] buffer)
{
if (client == null) throw new ArgumentNullException("tcpClient");
if (buffer == null) throw new ArgumentNullException("buffer");
Client = client;
Buffer = buffer;
Temp = string.Empty;
}
/// <summary>
/// Gets the TCP Client
/// </summary>
public TcpClient Client { get; private set; }
/// <summary>
/// Gets the Buffer.
/// </summary>
public byte[] Buffer { get; private set; }
/// <summary>
/// TCP接收到但未处理的数据
/// </summary>
public string Temp { get; set; }
/// <summary>
/// Gets the network stream
/// </summary>
public NetworkStream GetStream
{
get
{
NetworkStream stream = Client.GetStream();
stream.ReadTimeout = 5000;
stream.WriteTimeout = 5000;
return stream;
}
}
public bool IsRead { get; set; }
public object IsReadLock { get; } = new();
}

29
JiLinApp.Docking/VibrateAlarm/Service/TcpDatagramReceivedEventArgs.cs

@ -0,0 +1,29 @@
namespace JiLinApp.Docking.VibrateAlarm;
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <typeparam name="T">报文类型</typeparam>
public class TcpDatagramReceivedEventArgs<T> : EventArgs
{
/// <summary>
/// 客户端
/// </summary>
public TcpClientState ClientState { get; private set; }
/// <summary>
/// 报文
/// </summary>
public T Datagram { get; private set; }
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public TcpDatagramReceivedEventArgs(TcpClientState tcpClient, T datagram)
{
ClientState = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
Datagram = datagram;
}
}

765
JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs

@ -0,0 +1,765 @@
using EC.Util.Common;
using System.Collections.Concurrent;
using System.Net;
using System.Timers;
using Timer = System.Timers.Timer;
namespace JiLinApp.Docking.VibrateAlarm;
public class TcpManager
{
#region Fields
private AsyncTcpServer Server { get; set; }
private TcpManagerConfig Config { get; set; }
private ConcurrentDictionary<string, ClientMessage> DeviceDict { get; } = new();
private Timer HeartTimer { get; } = new();
#region Event
public delegate void VibrateTcpDeviceStateEvent(ClientMessage device);
public delegate void VibrateTcpSensorStateEvent(SensorState sensor);
public delegate void VibrateTcpAlarmEvent(TcpAlarmHostMessage msg);
public event VibrateTcpDeviceStateEvent? OnVibrateTcpDeviceState;
public event VibrateTcpSensorStateEvent? OnVibrateTcpSensorState;
public event VibrateTcpAlarmEvent? OnVibrateTcpAlarm;
#endregion Event
#endregion Fields
public TcpManager()
{
}
#region Server
public void Start(TcpManagerConfig config)
{
if (IsRunning()) return;
Server = new(IPAddress.Any, config.ServerPort);
Server.ClientConnected += Server_ClientConnected;
Server.ClientDisconnected += Server_ClientDisconnected;
Server.DatagramReceived += Server_DatagramReceived;
Server.Start();
HeartTimer.Close();
HeartTimer.Interval = 1000 * config.DeviceHeartKeep;
HeartTimer.Elapsed += HeartTimer_Elapsed;
HeartTimer.Start();
Config = config;
}
public void Stop()
{
if (!IsRunning()) return;
try
{
Server.Stop();
}
finally
{
Server.ClientConnected -= Server_ClientConnected;
Server.ClientDisconnected -= Server_ClientDisconnected;
Server.DatagramReceived -= Server_DatagramReceived;
Server = null;
DeviceDict.Clear();
HeartTimer.Stop();
HeartTimer.Elapsed -= HeartTimer_Elapsed;
}
}
public bool IsRunning()
{
return Server != null && Server.IsRunning();
}
#endregion Server
#region Events
private void HeartTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
foreach (var clientMsg in DeviceDict.Values)
{
if (!clientMsg.IsOnline) continue;
SendHostHeart_01(clientMsg);
}
}
private void Server_ClientConnected(object? sender, TcpClientConnectedEventArgs e)
{
string clientAddr = e.TcpClient.ClientAddr();
if (clientAddr == ":") return;
string clientIp = clientAddr.Split(':')[0];
string clientPort = clientAddr.Split(':')[1];
Console.WriteLine("主机上线:{0}", clientIp);
if (!TryGetDevice(clientIp, out ClientMessage clientMsg))
{
clientMsg = new()
{
Client = e.TcpClient,
Host = new() { Id = -1, Ip = clientIp, Port = clientPort }
};
AddDeivce(clientIp, clientMsg);
}
else
{
ProcessDeviceStateEvent(ref clientMsg, 1);
}
}
private void Server_ClientDisconnected(object? sender, TcpClientDisconnectedEventArgs e)
{
string clientAddr = e.TcpClient.ClientAddr();
if (clientAddr == ":") return;
string clientIp = clientAddr.Split(':')[0];
string clientPort = clientAddr.Split(':')[1];
Console.WriteLine("主机下线:{0}", clientIp);
if (TryGetDevice(clientIp, out ClientMessage clientMsg))
{
ProcessDeviceStateEvent(ref clientMsg, 0);
}
}
private void Server_DatagramReceived(object? sender, TcpDatagramReceivedEventArgs<byte[]> e)
{
string clientAddr = e.ClientState.Client.ClientAddr();
if (clientAddr == ":") return;
string clientIp = clientAddr.Split(':')[0];
string clientPort = clientAddr.Split(':')[1];
if (TryGetDevice(clientIp, out ClientMessage clientMsg))
{
if (clientMsg.Client == null || !clientMsg.Client.Connected) clientMsg.Client = e.ClientState.Client;
clientMsg.Host ??= new() { Id = -1, Ip = clientIp, Port = clientPort };
clientMsg.AddData(e.Datagram);
ProcessDeviceStateEvent(ref clientMsg, 1);
AnalysisClientMessage(ref clientMsg);
}
}
#endregion Events
#region Analysis
private void AnalysisClientMessage(ref ClientMessage clientMsg)
{
List<byte[]> msgList = clientMsg.GetMessageList();
foreach (byte[] msg in msgList)
{
bool vaild = msg.Length >= 19 && msg[0] == 0xAA && msg[1] == 0xAA;
Console.WriteLine("Recv from {0}:{1} => {2}, vaild:{3}", clientMsg.ClientIp, clientMsg.ClientPort, DataMessage.ToHexString(msg), vaild);
if (!vaild) continue;
DataMessage mm = new();
mm.Decode(msg);
if (clientMsg.Host.Id != mm.DeviceId) clientMsg.Host.Id = mm.DeviceId;
switch (mm.FunctionNum)
{
case 0x00:
Console.WriteLine("主机登录:{0}", clientMsg.ClientAddr);
ResponseHostLogin_10(clientMsg, mm);
RequestHostAutoUploadState_24(clientMsg);
RequestSensorList_07(clientMsg);
break;
case 0x01:
Console.WriteLine("主机心跳:{0}", clientMsg.ClientAddr);
break;
case 0x12:
Console.WriteLine("传感器地址设置响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x02);
break;
case 0x13:
Console.WriteLine("传感器模式设置响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x03);
break;
case 0x14:
Console.WriteLine("传感器轮询状态响应:{0}", clientMsg.ClientAddr);
int sensorAddr = mm.Data[0] + mm.Data[1] * 256;
int state = mm.Data[2] + mm.Data[3] * 256;
ProcessSensorStateEvent(ref clientMsg, mm, sensorAddr, state);
SetDataResponse(mm, 0x04);
break;
case 0x15:
Console.WriteLine("传感器复位响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x05);
break;
case 0x17:
Console.WriteLine("主机返回传感器列表:{0}", clientMsg.ClientAddr);
for (int j = 2; j < mm.Data.Length; j++)
{
sensorAddr = Convert.ToByte((mm.Data[j] + mm.Data[++j] * 256));
if (clientMsg.SensorDict.ContainsKey(sensorAddr)) continue;
SensorState sensor = new(mm.DeviceId, sensorAddr, 0, 0);
clientMsg.SensorDict[sensorAddr] = sensor;
}
RequestSensorsState_04(clientMsg);
SetDataResponse(mm, 0x07);
break;
case 0x18:
Console.WriteLine("传感器主动状态响应:{0}", clientMsg.ClientAddr);
sensorAddr = mm.Data[0] + mm.Data[1] * 256;
state = mm.Data[2] + mm.Data[3] * 256;
ProcessSensorStateEvent(ref clientMsg, mm, sensorAddr, state);
ResponseHostUploadState_08(clientMsg, mm);
break;
case 0x19:
Console.WriteLine("传感器关闭响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x09);
break;
case 0x30:
Console.WriteLine("传感器启动响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x20);
break;
case 0x31:
Console.WriteLine("传感器全部启动响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x21);
break;
case 0x34:
Console.WriteLine("主机启动自动上传响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x24);
break;
case 0x35:
Console.WriteLine("主机关闭自动上传响应:{0}", clientMsg.ClientAddr);
SetDataResponse(mm, 0x25);
break;
}
}
}
private void ProcessDeviceStateEvent(ref ClientMessage device, int online)
{
if (device.OnlineState != online)
{
device.OnlineState = online;
ReportDeviceState(device);
}
}
private void ProcessSensorStateEvent(ref ClientMessage client, DataMessage mm, int sensorAddr, int state)
{
int offline = state % 2;
int alarm = state / 2 % 2;
if (!client.SensorDict.TryGetValue(sensorAddr, out SensorState? sensor))
{
sensor = new(mm.DeviceId, sensorAddr, offline, alarm);
client.SensorDict[sensorAddr] = sensor;
ReportSensorState(sensor);
}
else if (sensor.OfflineState != offline || sensor.AlarmState != alarm)
{
sensor.OfflineState = offline;
sensor.AlarmState = alarm;
ReportSensorState(sensor);
}
if (alarm == 1)
{
ReportAlarm(sensor.DeviceId, sensorAddr);
RequestSensorReset_05(client, sensorAddr);
}
}
private void ReportDeviceState(ClientMessage device)
{
OnVibrateTcpDeviceState?.Invoke(device);
}
private void ReportSensorState(SensorState sensor)
{
OnVibrateTcpSensorState?.Invoke(sensor);
}
private void ReportAlarm(int deviceId, int sensorAddr)
{
TcpAlarmHostMessage alarmMsg = new()
{
AlarmTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
CID = "1151",
DeviceID = deviceId,
SensorAddr = sensorAddr.ToString()
};
OnVibrateTcpAlarm?.Invoke(alarmMsg);
}
#endregion Analysis
#region Send
/// <summary>
/// 0x10:主机响应0x00登录
/// </summary>
/// <param name="client"></param>
/// <param name="mm"></param>
/// <returns></returns>
public bool ResponseHostLogin_10(ClientMessage client, DataMessage mm)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x10, 0);
return SendResponse(msg, mm.FrameNum);
}
/// <summary>
/// 0x01:心跳
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool SendHostHeart_01(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x01, 1);
msg.Data = new byte[] { 0xFF };
return SendNoRequest(msg);
}
/// <summary>
/// 0x02:设置传感器地址
/// </summary>
/// <param name="client"></param>
/// <param name="newSensorAddr"></param>
/// <returns></returns>
public bool RequestSensorChange_02(ClientMessage client, int newSensorAddr)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x02, 4);
msg.Data = new byte[] { (byte)(newSensorAddr % 256), (byte)(newSensorAddr / 256), (100 % 256), (100 / 256) };
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x04:请求传感器状态
/// </summary>
/// <param name="client"></param>
/// <param name="sensorAddr"></param>
/// <returns></returns>
public bool RequestSensorState_04(ClientMessage client, int sensorAddr)
{
if (!client.IsOnline) return false;
if (!client.SensorDict.TryGetValue(sensorAddr, out _)) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x04, 2);
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x04:请求传感器状态
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorsState_04(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x04, 2);
foreach (var item in client.SensorDict.Values)
{
int sensorAddr = item.Addr;
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
SendRequestTry(ref request);
}
return true;
}
/// <summary>
/// 0x05:复位传感器
/// </summary>
/// <param name="client"></param>
/// <param name="sensorAddr"></param>
/// <returns></returns>
public bool RequestSensorReset_05(ClientMessage client, int sensorAddr)
{
if (!client.IsOnline) return false;
if (!client.SensorDict.TryGetValue(sensorAddr, out _)) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x05, 2);
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x07:请求传感器列表
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorList_07(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x07, 0);
DataRequest request = new()
{
Request = msg
};
return SendRequestMust(ref request);
}
/// <summary>
/// 0x08:主机响应0x18主动上传状态
/// </summary>
/// <param name="client"></param>
/// <param name="mm"></param>
/// <returns></returns>
public bool ResponseHostUploadState_08(ClientMessage client, DataMessage mm)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x08, 2);
msg.Data = new byte[] { mm.Data[0], mm.Data[1] };
return SendResponse(msg, mm.FrameNum);
}
/// <summary>
/// 0x09:关闭传感器
/// </summary>
/// <param name="client"></param>
/// <param name="sensorAddr"></param>
/// <returns></returns>
public bool RequestSensorTurnOff_09(ClientMessage client, int sensorAddr)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x09, 2);
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x09:关闭传感器
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorsTurnOff_09(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x09, 2);
foreach (var item in client.SensorDict.Values)
{
int sensorAddr = item.Addr;
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
SendRequestTry(ref request);
}
return true;
}
/// <summary>
/// 0x20:启动传感器
/// </summary>
/// <param name="client"></param>
/// <param name="sensorAddr"></param>
/// <returns></returns>
public bool RequestSensorTurnOn_20(ClientMessage client, int sensorAddr)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x20, 2);
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x20:启动传感器
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorsTurnOn_20(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x20, 2);
foreach (var item in client.SensorDict.Values)
{
int sensorAddr = item.Addr;
msg.Data = new byte[] { (byte)(sensorAddr % 256), (byte)(sensorAddr / 256) };
DataRequest request = new()
{
Request = msg
};
SendRequestTry(ref request);
}
return true;
}
/// <summary>
/// 0x21:启动全部传感器
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorsTurnOn_21(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x21, 0);
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
/// <summary>
/// 0x24:启动自动上传
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestHostAutoUploadState_24(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x24, 0);
DataRequest request = new()
{
Request = msg
};
return SendRequestMust(ref request);
}
/// <summary>
/// 0x25:关闭自动上传
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public bool RequestSensorsTurnOn_25(ClientMessage client)
{
if (!client.IsOnline) return false;
DataMessage msg = GetSendMessageHead(client.Host.Id, client, 0x25, 0);
DataRequest request = new()
{
Request = msg
};
return SendRequestTry(ref request);
}
#endregion Send
#region ClientMessage
public bool ContainsDevice(string clientIp)
{
return DeviceDict.ContainsKey(clientIp);
}
public ClientMessage GetDevice(string clientIp)
{
return DeviceDict[clientIp];
}
public bool TryGetDevice(string clientIp, out ClientMessage clientMsg)
{
return DeviceDict.TryGetValue(clientIp, out clientMsg);
}
public bool TryGetDevice(int deviceId, out ClientMessage clientMsg)
{
foreach (var item in DeviceDict.Values)
{
if (item.Host.Id == deviceId)
{
clientMsg = item;
return true;
}
}
clientMsg = null;
return false;
}
private bool AddDeivce(string clientIp, ClientMessage clientMsg)
{
if (ContainsDevice(clientIp)) return false;
DeviceDict[clientIp] = clientMsg;
return true;
}
private void SetDevice(string clientIp, ClientMessage clientMsg)
{
DeviceDict[clientIp] = clientMsg;
}
private bool RemoveDevice(string clientIp)
{
return DeviceDict.Remove(clientIp, out _);
}
public List<ClientMessage> GetDeviceList()
{
return DeviceDict.Values.ToList();
}
#endregion ClientMessage
#region Set
private byte Frame { get; set; } = 0;
private byte FrameInc
{ get { return (byte)(++Frame % byte.MaxValue); } }
private int SendTryTime { get; set; } = 5;
private int SendTryInterval { get; set; } = 200;
private int ReqWaitTime { get; set; } = 3 * 10;
private int ReqWaitInterval { get; set; } = 100;
private DataMessage GetSendMessageHead(int deviceId, ClientMessage client, byte funNum, byte dataLen)
{
DataMessage msg = new()
{
DeviceId = deviceId,
SendIp = client.LocalIp,
SendPort = client.LocalPort.ToInt(),
ReceiveIp = client.ClientIp,
ReceivePort = client.ClientPort.ToInt(),
FunctionNum = funNum,
DataLen = dataLen
};
return msg;
}
private bool SendMessage(string ip, byte[] bytes)
{
if (Server == null || !Server.IsRunning()) return false;
if (!TryGetDevice(ip, out ClientMessage clientMsg)) return false;
string cmd = DataMessage.ToHexString(bytes);
bool send = false;
for (int i = 0; i < SendTryTime; i++)
{
send = Server.SendAsync(clientMsg.Client, bytes);
if (send) break;
Thread.Sleep(SendTryInterval);
}
Console.WriteLine("Send to {0}:{1} => {2}, send:{3}", clientMsg.ClientIp, clientMsg.ClientPort, cmd, send);
return send;
}
private bool SendRequestTry(ref DataRequest request)
{
if (request.Request == null) return false;
request.Request.FrameNum = FrameInc;
bool send = SendMessage(request.Request.ReceiveIp, request.Request.Encode());
if (!send) return false;
bool respond = false;
SetDataRequest(request);
for (int i = 0; i < ReqWaitTime; i++)
{
respond = IsResponse(request);
if (respond) break;
Thread.Sleep(ReqWaitInterval);
}
RemoveDataRequest(request);
return respond;
}
private bool SendRequestMust(ref DataRequest request)
{
if (request.Request == null) return false;
request.Request.FrameNum = FrameInc;
bool send, respond = false;
do
{
send = SendMessage(request.Request.ReceiveIp, request.Request.Encode());
if (!send) continue;
SetDataRequest(request);
for (int i = 0; i < ReqWaitTime; i++)
{
respond = IsResponse(request);
if (respond) break;
Thread.Sleep(ReqWaitInterval);
}
} while (!send && !respond);
RemoveDataRequest(request);
return true;
}
private bool SendNoRequest(DataMessage request)
{
request.FrameNum = FrameInc;
bool send = SendMessage(request.ReceiveIp, request.Encode());
return send;
}
private bool SendResponse(DataMessage request, byte frameNum)
{
request.FrameNum = frameNum;
bool send = SendMessage(request.ReceiveIp, request.Encode());
return send;
}
private ConcurrentDictionary<string, DataRequest> DataRequestDict { get; } = new();
private void SetDataRequest(DataRequest request)
{
string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}";
DataRequestDict[key] = request;
}
private void SetDataResponse(DataMessage msg, byte funcNum)
{
string key = $"{funcNum}-{msg.FrameNum}";
DataRequest? item = DataRequestDict[key];
if (item != null) item.Responce = msg;
}
private void RemoveDataRequest(DataRequest request)
{
string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}";
DataRequestDict.Remove(key);
}
private bool IsResponse(DataRequest request)
{
string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}";
DataRequest? item = DataRequestDict[key];
return item != null && item.Responce != null;
}
#endregion Set
}

2222
JiLinApp.Docking/config/alarmcode.json

File diff suppressed because it is too large

12
JiLinCpApp.sln

@ -7,6 +7,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiLinApp", "JiLinApp\JiLinA
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC.Util", "EC.Util\EC.Util.csproj", "{B5D7ED30-805C-4A5D-BD41-D2F910806340}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC.Util", "EC.Util\EC.Util.csproj", "{B5D7ED30-805C-4A5D-BD41-D2F910806340}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiLinApp.Biz", "JiLinApp.Biz\JiLinApp.Biz.csproj", "{F58E724C-5F85-4A90-A1B9-45191D573D22}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiLinApp.Docking", "JiLinApp.Docking\JiLinApp.Docking.csproj", "{EA25A38F-CE70-4C41-B332-B10C2CECE1B5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -21,6 +25,14 @@ Global
{B5D7ED30-805C-4A5D-BD41-D2F910806340}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.Build.0 = Release|Any CPU {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.Build.0 = Release|Any CPU
{F58E724C-5F85-4A90-A1B9-45191D573D22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F58E724C-5F85-4A90-A1B9-45191D573D22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F58E724C-5F85-4A90-A1B9-45191D573D22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F58E724C-5F85-4A90-A1B9-45191D573D22}.Release|Any CPU.Build.0 = Release|Any CPU
{EA25A38F-CE70-4C41-B332-B10C2CECE1B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA25A38F-CE70-4C41-B332-B10C2CECE1B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA25A38F-CE70-4C41-B332-B10C2CECE1B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA25A38F-CE70-4C41-B332-B10C2CECE1B5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

Loading…
Cancel
Save