You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
15 KiB
421 lines
15 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Net.Sockets;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Infrastructure.Extensions;
|
|
using Infrastructure.Models;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Polly;
|
|
|
|
namespace LiChuangService
|
|
{
|
|
public class DeviceService : IDisposable
|
|
{
|
|
private readonly IHostingEnvironment _env;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IServiceProvider _applicationServices;
|
|
private CancellationTokenSource _tokenSource;
|
|
private static readonly object lockObject = new Object();
|
|
private readonly TcpClient _tcpClient;
|
|
private string _tcpIp;
|
|
private int _tcpPort;
|
|
private byte[] _address;
|
|
private string _addressValue;
|
|
private readonly Dictionary<int, int> _types = new Dictionary<int, int>();
|
|
|
|
public DeviceService(IHostingEnvironment env, IServiceProvider applicationServices, IConfiguration configuration, IHttpClientFactory httpClientFactory)
|
|
{
|
|
this._env = env;
|
|
this._applicationServices = applicationServices;
|
|
this._configuration = configuration;
|
|
this._httpClientFactory = httpClientFactory;
|
|
this._tokenSource = new CancellationTokenSource();
|
|
this._tcpClient = new TcpClient();
|
|
this._tcpIp = this._configuration["tcp.ip"];
|
|
this._tcpPort = this._configuration.GetValue<int>("tcp.port");
|
|
this.Connect();
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
Task.Run(async () =>
|
|
{
|
|
while (!_tokenSource.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
Console.WriteLine("timer ...");
|
|
Notify();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ex.PrintStack();
|
|
}
|
|
await Task.Delay(this._configuration.GetValue<int>("timer.seconds") * 1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void Notify()
|
|
{
|
|
Console.WriteLine("notify start ...");
|
|
try
|
|
{
|
|
this.CheckConnection();
|
|
this.SearchAddress();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ex.PrintStack();
|
|
}
|
|
Console.WriteLine("notify end ...");
|
|
}
|
|
|
|
public void Switch2AllOn(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x0f, 0x00, 0x00, 0x00, 0x03, 0x01, 0x07 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void Switch2AllOff(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x0f, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void Switch2L1On(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0xff, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void Switch2L1Off(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0x00, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void Switch2L2On(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x01, 0xff, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void Switch2L2Off(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x01, 0x00, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x02);
|
|
}
|
|
|
|
public void SocketOn(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0xff, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x10);
|
|
}
|
|
|
|
public void SocketOff(string id)
|
|
{
|
|
this.Command16(new byte[] { this.GetNumber(id), 0x05, 0x00, 0x00, 0x00, 0x00 });
|
|
this.SearchSwitchStatus(this.GetNumber(id), 0x10);
|
|
}
|
|
|
|
private byte GetNumber(string id)
|
|
{
|
|
return Convert.ToByte(id.Split("-")[1]);
|
|
}
|
|
|
|
private void CheckConnection()
|
|
{
|
|
var tcpIp = this._configuration["tcp.ip"];
|
|
var tcpPort = this._configuration.GetValue<int>("tcp.port");
|
|
if (this._tcpIp != tcpIp || this._tcpPort != tcpPort)
|
|
{
|
|
this._tcpIp = tcpIp;
|
|
this._tcpPort = tcpPort;
|
|
this.Connect();
|
|
}
|
|
else
|
|
{
|
|
if (!this._tcpClient.Connected)
|
|
{
|
|
this.Connect();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Connect()
|
|
{
|
|
if (this._tcpClient.Connected)
|
|
{
|
|
this._tcpClient.Close();
|
|
}
|
|
try
|
|
{
|
|
this._tcpClient.Connect(this._tcpIp, this._tcpPort);
|
|
new Thread(() =>
|
|
{
|
|
while (this._tcpClient.Connected)
|
|
{
|
|
var stream = this._tcpClient.GetStream();
|
|
if (stream.CanRead)
|
|
{
|
|
var buffer = new byte[512];
|
|
var length = stream.Read(buffer);
|
|
var data = buffer.ToList().Take(length).ToArray();
|
|
this.Handle(data);
|
|
}
|
|
}
|
|
}).Start();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ex.PrintStack();
|
|
}
|
|
}
|
|
|
|
private void SearchAddress()
|
|
{
|
|
var list = new List<byte>() { 0x0c };
|
|
var command = Command(list.ToArray());
|
|
this.Write(command);
|
|
}
|
|
|
|
private void Handle(byte[] data)
|
|
{
|
|
Console.WriteLine($"read:{BitConverter.ToString(data).Replace("-", " ")}");
|
|
var type = data[2];
|
|
var command = data[4];
|
|
if (command == 0x0c)//16位地址
|
|
{
|
|
this._address = data.ToList().Skip(5).Take(8).ToArray();
|
|
this._addressValue = BitConverter.ToString(this._address).Replace("-", "");
|
|
this.SearchNodeStatus();
|
|
}
|
|
else if (command == 0x15)//节点状态
|
|
{
|
|
var status = data.ToList().Skip(14).Take(100).ToArray();
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
if (status[i] == 0x01)
|
|
{
|
|
Console.WriteLine($"node {i + 1} online");
|
|
this.SearchNodeType(i + 1);
|
|
}
|
|
else if (status[i] == 0x02)
|
|
{
|
|
Console.WriteLine($"node {i + 1} offline");
|
|
}
|
|
}
|
|
}
|
|
else if (command == 0x17)//读取
|
|
{
|
|
var nodeNumber = data[14];
|
|
if (data.Length == 22 && data[18] == 0xff)//读类型命令返回值
|
|
{
|
|
var nodeType = data[17];
|
|
this._types[nodeNumber] = nodeType;
|
|
if (nodeType == 0x02)
|
|
{
|
|
Console.WriteLine($"node {nodeNumber} is switch 2");
|
|
this.SearchSwitchStatus(nodeNumber, 0x02);
|
|
}
|
|
else if (nodeType == 0x03)
|
|
{
|
|
Console.WriteLine($"node {nodeNumber} is socket 1");
|
|
this.SearchSwitchStatus(nodeNumber, 0x10);
|
|
}
|
|
else if (nodeType == 0x04)
|
|
{
|
|
Console.WriteLine($"node {nodeNumber} is socket 2");
|
|
this.SearchSwitchStatus(nodeNumber, 0x10);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"unknown type:{nodeType}");
|
|
}
|
|
}
|
|
if (data[15] == 0x01)
|
|
{
|
|
var nodeType = this._types[nodeNumber];
|
|
var statusData = data[17];
|
|
NotifyModel model = null;
|
|
if (nodeType == 0x02)
|
|
{
|
|
Console.WriteLine($"~~~node {nodeNumber},switch 2 status:{statusData}");
|
|
|
|
var switchStatus = statusData.ToBitString();
|
|
model = CreateModel("二路灯开关", this._addressValue + "-" + nodeNumber, "switch2", "switch");
|
|
model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status1", Name = "L1状态", Value = switchStatus[7] == '1' ? "开" : "关", DisplayOrder = 1 });
|
|
model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status2", Name = "L2状态", Value = switchStatus[6] == '1' ? "开" : "关", DisplayOrder = 2 });
|
|
}
|
|
else if (nodeType == 0x03)
|
|
{
|
|
Console.WriteLine($"~~~node {nodeNumber},socket 1 status:{statusData}");
|
|
model = CreateModel("一路插座", this._addressValue + "-" + nodeNumber, "socket1", "socket");
|
|
model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status", Name = "状态", Value = statusData == 0x01 ? "开" : "关", DisplayOrder = 1 });
|
|
}
|
|
else if (nodeType == 0x04)
|
|
{
|
|
Console.WriteLine($"~~~node {nodeNumber},socket 2 status:{statusData}");
|
|
model = CreateModel("二路插座", this._addressValue + "-" + nodeNumber, "socket2", "switch");
|
|
model.Data.Add(new DataModel { Type = DataValueType.Text.ToString(), Key = "status", Name = "状态", Value = statusData == 0x01 ? "开" : "关", DisplayOrder = 1 });
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"unknown type:{nodeType}");
|
|
}
|
|
if (model != null)
|
|
{
|
|
this.NotifyServer(model);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SearchNodeStatus()
|
|
{
|
|
var list = new List<byte>() { 0x15 };
|
|
list.AddRange(this._address);
|
|
list.Add(0x00);
|
|
var command = Command(list.ToArray());
|
|
this.Write(command);
|
|
}
|
|
|
|
private void SearchNodeType(int i)
|
|
{
|
|
var list = new List<byte>() { 0x16 };
|
|
list.AddRange(this._address);
|
|
list.Add(0x00);
|
|
var modbusdata = new List<byte> {
|
|
(byte)i,0x03,0x00,0x0f,0x00,0x01
|
|
}.ToArray();
|
|
var modbus = new List<byte>();
|
|
modbus.AddRange(modbusdata);
|
|
var crc = Crc16.ComputeChecksum(modbusdata);
|
|
modbus.Add(crc[1]);
|
|
modbus.Add(crc[0]);
|
|
list.AddRange(modbus);
|
|
var command = Data(list.ToArray());
|
|
this.Write(command);
|
|
}
|
|
|
|
private void SearchSwitchStatus(int i, byte cmd)
|
|
{
|
|
var list = new List<byte>() { 0x16 };
|
|
list.AddRange(this._address);
|
|
list.Add(0x00);
|
|
var modbusdata = new List<byte> {
|
|
(byte)i, 0x01,0x00,0x00,0x00,cmd
|
|
}.ToArray();
|
|
var modbus = new List<byte>();
|
|
modbus.AddRange(modbusdata);
|
|
var crc = Crc16.ComputeChecksum(modbusdata);
|
|
modbus.Add(crc[1]);
|
|
modbus.Add(crc[0]);
|
|
list.AddRange(modbus);
|
|
var command = Data(list.ToArray());
|
|
this.Write(command);
|
|
}
|
|
|
|
private byte[] Command(byte[] data)
|
|
{
|
|
var command = new List<byte> { 0xfe, 0xa5, 0x00 };
|
|
command.Add((byte)data.Length);
|
|
command.AddRange(data);
|
|
command.Add((byte)command.Skip(2).Select(o => Convert.ToInt32(o)).Sum());
|
|
return command.ToArray();
|
|
}
|
|
|
|
private byte[] Data(byte[] data, bool crc16 = false)
|
|
{
|
|
var command = new List<byte> { 0xfe, 0xa5, 0x01 };
|
|
command.Add((byte)data.Length);
|
|
command.AddRange(data);
|
|
if (crc16)
|
|
{
|
|
var crc = Crc16.ComputeChecksum(data);
|
|
command.Add(crc[1]);
|
|
command.Add(crc[0]);
|
|
}
|
|
else
|
|
{
|
|
command.Add((byte)command.Skip(2).Select(o => Convert.ToInt32(o)).Sum());
|
|
}
|
|
return command.ToArray();
|
|
}
|
|
|
|
private void Command16(byte[] command)
|
|
{
|
|
var list = new List<byte>() { 0x16 };
|
|
list.AddRange(this._address);
|
|
list.Add(0x00);
|
|
var modbusdata = command;
|
|
var modbus = new List<byte>();
|
|
modbus.AddRange(modbusdata);
|
|
var crc = Crc16.ComputeChecksum(modbusdata);
|
|
modbus.Add(crc[1]);
|
|
modbus.Add(crc[0]);
|
|
list.AddRange(modbus);
|
|
var data = Data(list.ToArray());
|
|
this.Write(data);
|
|
}
|
|
|
|
private void Write(byte[] command)
|
|
{
|
|
Thread.Sleep(200);
|
|
Console.WriteLine($"write:{BitConverter.ToString(command).Replace("-", " ")}");
|
|
this._tcpClient.GetStream().Write(command);
|
|
}
|
|
|
|
private NotifyModel CreateModel(string name, string number, string path, string icon)
|
|
{
|
|
var host = string.IsNullOrEmpty(this._configuration["server.ip"]) ? Helper.Instance.GetLocalIP().ToString() : this._configuration["server.ip"];
|
|
var port = Convert.ToInt32(Regex.Match(this._configuration["server.urls"], @"(?<=:)\d+").Value);
|
|
|
|
return new NotifyModel
|
|
{
|
|
CategoryName = "电器",
|
|
CategoryNumber = "20",
|
|
Name = name,
|
|
Number = number,
|
|
Icon = icon,
|
|
IsOnline = true,
|
|
BaseUrl = $"http://{host}:{port}/{path}",
|
|
ApiPath = "/api"
|
|
};
|
|
}
|
|
|
|
private void NotifyServer(NotifyModel model)
|
|
{
|
|
var url = $"http://{this._configuration["node.url"]}/Notify";
|
|
Console.WriteLine(url);
|
|
var hc = this._httpClientFactory.CreateClient();
|
|
var task = this._httpClientFactory.CreateClient().PostAsync(url, new FormUrlEncodedContent(model.ToList()));
|
|
task.Wait();
|
|
using (var response = task.Result)
|
|
{
|
|
using (var content = response.Content)
|
|
{
|
|
var value = content.ReadAsStringAsync().Result;
|
|
Console.WriteLine($"end:{url}:{value}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Console.WriteLine("LiChuangService dispose...");
|
|
if (this._tcpClient.Connected)
|
|
{
|
|
this._tcpClient.Close();
|
|
}
|
|
this._tokenSource.Cancel();
|
|
}
|
|
}
|
|
} |