fajiao
2 years ago
49 changed files with 7508 additions and 7 deletions
@ -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> |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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, |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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
|
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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 |
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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> |
@ -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; } |
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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 }; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 }; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 |
||||
|
} |
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
namespace JiLinApp.Docking.VibrateAlarm; |
||||
|
|
||||
|
public class DataRequest |
||||
|
{ |
||||
|
public DataMessage Request { get; set; } |
||||
|
|
||||
|
public DataMessage Responce { get; set; } |
||||
|
|
||||
|
public DataRequest() |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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
|
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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)); |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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
|
||||
|
} |
File diff suppressed because it is too large
Loading…
Reference in new issue