using Edge.Core.Parser.BinaryParser.Util; using Edge.Core.Processor; using Edge.Core.UniversalApi; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using PetroChinaOnlineWatchPlugin.MessageEntity; using PetroChinaOnlineWatchPlugin.MessageEntity.Outgoing; using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; namespace PetroChinaOnlineWatchPlugin { internal class ConnectionController { public int TcpListenPort { get { return 6000; } } public TcpClient TcpClientInstance { get { return tcpClient; } } public static ConnectionController Default => instance; private static readonly ConnectionController instance = new ConnectionController(); private ILogger logger = NullLogger.Instance; private IProcessor source; private UniversalApiHub universalApiHub; private TcpListener tcpListener; private TcpClient tcpClient = new TcpClient(); //消息长度不应大于228字节,以减小对应用缓冲区大小的要求。 private byte[] tcpServerReceivedBuffer = new byte[1024]; private byte[] tcpClientReceivedBuffer = new byte[1024]; private AppConfigV1 appConfig; private bool hasSentConfig = false; private int environmentPushingInterval = -1; private const string OnUploadOnlineMonitorDataStateChangeEventName = "OnUploadOnlineMonitorDataStateChange"; public bool Start(AppConfigV1 appConfig, ILogger logger, IProcessor source, UniversalApiHub universalApiHub) { this.appConfig = appConfig; this.logger = logger; this.source = source; this.universalApiHub = universalApiHub; HeartBeatUdpProxy.Default.OnDataReceived += Udp_DataReceived; logger.LogInformation($"TcpListener is listening on port, {TcpListenPort}"); tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, TcpListenPort)); tcpListener.Start(); tcpListener.BeginAcceptTcpClient(AcceptCallback, null); return true; } public void SendUnsolicitedMessage() { try { if (tcpClient.Client == null || !tcpClient.Connected) { logger.LogDebug($"在线监测所有数据上传失败:无法连接到远程服务器"); return; } dynamic dataList = null; var tempDic = new Dictionary(); var span = DateTime.Now - DateTime.Today; //如果配置数据无变更,每日零时和中午12点上传该配置信息。 if (span.TotalSeconds < 8 || (span.TotalSeconds > 43200 && span.TotalSeconds < 43208)) { if (!hasSentConfig) { dataList = SqlClientHelper.ReadOnlineMonitorData(appConfig, logger, ref tempDic, DbAddressType.Configure); if (dataList != null) { // CF_ID 01H, CF_Message var configMessage = new AnswerWriteUnsolicitedMessageOut(appConfig, 0x01, dataList); SendOnlineMonitorData("配置数据", configMessage); hasSentConfig = true; } } } else if (hasSentConfig) { hasSentConfig = false; } if (environmentPushingInterval < 0) { //环境实时,数据采集时间间隔不大于 30 秒 environmentPushingInterval = 29; dataList = SqlClientHelper.ReadOnlineMonitorData(appConfig, logger, ref tempDic, DbAddressType.EnvironmentData); if (dataList != null) { var environmentRealtime = new AnswerWriteUnsolicitedMessageOut(appConfig, 0x11, dataList); SendOnlineMonitorData("环境数据", environmentRealtime); SqlClientHelper.UpdateOnlineMonitorData(appConfig, logger); } } else { environmentPushingInterval -= 1; } dataList = SqlClientHelper.ReadOnlineMonitorData(appConfig, logger, ref tempDic, DbAddressType.PumpData); if (dataList != null) { var pumpRealtimeMessage = new AnswerWriteUnsolicitedMessageOut(appConfig, 0x21, dataList); SendOnlineMonitorData("加油枪数据", pumpRealtimeMessage); SqlClientHelper.UpdateOnlineMonitorData(appConfig, logger); } dataList = SqlClientHelper.ReadOnlineMonitorData(appConfig, logger, ref tempDic, DbAddressType.FaultData); if (dataList != null) { var faultMessage = new AnswerWriteUnsolicitedMessageOut(appConfig, 0x41, dataList); SendOnlineMonitorData("报警_故障数据", faultMessage); SqlClientHelper.UpdateOnlineMonitorData(appConfig, logger); } } catch (Exception ex) { logger.LogError($"Exception in SendUnsolicitedMessage(...):\r\n{ex}"); } } private void SendOnlineMonitorData(string dataTypeName, MessageBase sendMessage) { try { if (tcpClient.Client == null || !tcpClient.Connected) { logger.LogError($"{dataTypeName}上传失败:无法连接到远程服务器"); return; } byte[] sendBuffer = Parser.Default.Serialize(sendMessage); logger.LogDebug($"Tcp client sent data, {sendMessage.ToString()} in SendOnlineMonitorData"); tcpClient.Client.Send(sendBuffer); logger.LogDebug($"Tcp client sent data, 0x{sendBuffer.ToHexLogString()} in Udp_DataReceived"); var r = universalApiHub.FireEvent(source, OnUploadOnlineMonitorDataStateChangeEventName, new UploadState() { DataTypeName = dataTypeName }); } catch (Exception ex) { logger.LogDebug($"Exception in SendOnlineMonitorData(...):\r\n{ex}"); tcpClient.Close(); } } private void Udp_DataReceived(object sender, EventArgs ar) { try { var udpar = ar as UdpDataEventArgs; logger.LogDebug($"TcpClient connect to {udpar.IpAddress}, port {udpar.Port}"); tcpClient = new TcpClient(); tcpClient.Connect(udpar.IpAddress, udpar.Port); tcpClient.Client.BeginReceive(tcpClientReceivedBuffer, 0, tcpClientReceivedBuffer.Length, SocketFlags.None, TcpClientDataReceived, tcpClient); var tempDic = new Dictionary(); var dataList = SqlClientHelper.ReadOnlineMonitorData(appConfig, logger, ref tempDic, DbAddressType.Configure); if (dataList != null) { // CF_ID 01H, CF_Message var configMessage = new AnswerWriteUnsolicitedMessageOut(appConfig, 0x01, dataList); SendOnlineMonitorData("配置数据", configMessage); } } catch (Exception ex) { logger.LogError($"Exception in Udp_DataReceived(...):\r\n{ex}"); } } private void TcpClientDataReceived(IAsyncResult ar) { TcpClient client = null; try { client = ar.AsyncState as TcpClient; int number = client.Client.EndReceive(ar); ar.AsyncWaitHandle.Close(); var remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint; int messageLengthIndex = 6; for (int offset = 0; offset < number;) { var temp = new byte[] { tcpClientReceivedBuffer[offset + messageLengthIndex + 1], tcpClientReceivedBuffer[offset + messageLengthIndex] }; offset = messageLengthIndex + 2 + BitConverter.ToUInt16(temp, 0); byte[] messageBuffer = new byte[offset]; for (int i = 0; i < offset; i++) { messageBuffer[i] = tcpClientReceivedBuffer[i]; } logger.LogDebug($"Tcp client received data, 0x{messageBuffer.ToHexLogString()}, from IpAddress:Port, {remoteEndPoint.Address}:{remoteEndPoint.Port}"); dynamic receivedMessage = Parser.Default.Deserialize(messageBuffer); if (receivedMessage.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK) { logger.LogDebug($"Tcp client Incoming--->: {receivedMessage.ToString()}"); //ACK 0x00 肯定确认,收到数据 if (receivedMessage.MessageAck == 0x00) { logger.LogDebug($"SqlClientHelper "); //SqlClientHelper.UpdateOnlineMonitorData(appConfig, logger); } } } if (number > 0) { tcpClientReceivedBuffer = new byte[tcpClientReceivedBuffer.Length]; client.Client.BeginReceive(tcpClientReceivedBuffer, 0, tcpClientReceivedBuffer.Length, SocketFlags.None, TcpClientDataReceived, client); } else { logger.LogDebug($"Tcp client received 0 count data which indicates the connection is broken in TcpClientDataReceived!"); client.Close(); } } catch (Exception ex) { logger.LogError($"Exception in TcpClientDataReceived(...):\r\n{ex}"); client.Close(); } } private void TcpServerDataReceived(IAsyncResult ar) { TcpClient client = null; try { client = ar.AsyncState as TcpClient; int number = client.Client.EndReceive(ar); ar.AsyncWaitHandle.Close(); var remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint; logger.LogDebug($"Tcp server received {number} bytes, from IpAddress:Port, {remoteEndPoint.Address}:{remoteEndPoint.Port}"); int messageLengthIndex = 6; for (int offset = 0; offset < number;) { var temp = new byte[] { tcpServerReceivedBuffer[offset + messageLengthIndex + 1], tcpServerReceivedBuffer[offset + messageLengthIndex] }; offset = messageLengthIndex + 2 + BitConverter.ToUInt16(temp, 0); byte[] messageBuffer = new byte[offset]; for (int i = 0; i < offset; i++) { messageBuffer[i] = tcpServerReceivedBuffer[i]; } logger.LogDebug($"Tcp server received data, 0x{messageBuffer.ToHexLogString()}"); dynamic receivedMessage = Parser.Default.Deserialize(messageBuffer); logger.LogDebug($"Tcp server Incoming--->: {receivedMessage.ToString()}"); if (receivedMessage.MessageType == MessageType.IFSF_MESSAGE_TYPE_READ) { //控制设备读取设备的心跳间隔 if (receivedMessage.DatabaseAddress[0] == 0x00 && receivedMessage.DataIdentifier[0] == 0x04) { var dataList = new List() { new Bin8Message(0x04, 11) }; if (dataList != null) { // 心跳间隔为11秒 var configMessage = new AnswerWriteUnsolicitedMessageOut(appConfig, receivedMessage.MessageToken, receivedMessage.DatabaseAddress, dataList); byte[] sendBuffer = Parser.Default.Serialize(configMessage); logger.LogDebug($"Tcp server sent data, {configMessage.ToString()} in TcpServerDataReceived"); client.Client.Send(sendBuffer); logger.LogDebug($"Tcp server sent data, 0x{sendBuffer.ToHexLogString()} in TcpServerDataReceived"); } } } } if (number > 0) { tcpServerReceivedBuffer = new byte[tcpServerReceivedBuffer.Length]; client.Client.BeginReceive(tcpServerReceivedBuffer, 0, tcpServerReceivedBuffer.Length, SocketFlags.None, TcpServerDataReceived, client); } else { logger.LogDebug($"Tcp server received 0 count data which indicates the connection is broken in TcpServerDataReceived!"); client.Close(); } } catch (Exception ex) { logger.LogError($"Exception in TcpServerDataReceived(...):\r\n{ex}"); client.Close(); } } private void AcceptCallback(IAsyncResult ar) { try { var client = tcpListener.EndAcceptTcpClient(ar); client.Client.BeginReceive(tcpServerReceivedBuffer, 0, tcpServerReceivedBuffer.Length, SocketFlags.None, TcpServerDataReceived, client); var remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint; logger.LogDebug($"Accepted a remote tcp client from IpAddress:Port, {remoteEndPoint.Address}:{remoteEndPoint.Port}"); tcpListener.BeginAcceptTcpClient(AcceptCallback, null); } catch (Exception ex) { logger.LogError($"Exception in AcceptCallback(...):\r\n{ex}"); } } private decimal BytesToBcd(List data) { var strBcd = new StringBuilder(); int pointIndex = data[0]; for (int i = 1; i < data.Count; i++) { strBcd.Append(data[i]); if (i == pointIndex) strBcd.Append('.'); } return decimal.Parse(strBcd.ToString()); } } public class UploadState { public string DataTypeName { get; set; } /// /// each vr watching nozzle is a physical pump nozzle, here is the bind info, will be used to draw UI. /// public int SiteLevelNozzleId { get; set; } } }