/// <summary> /// Принять команду от SCADA-Сервера /// </summary> public bool ReceiveCommand(out KPLogic.Command cmd) { int kpNum; int cmdNum; double cmdVal; byte[] cmdData; if (ReceiveCommand(out kpNum, out cmdNum, out cmdVal, out cmdData)) { cmd = new KPLogic.Command(); cmd.CmdType = cmdData == null ? (double.IsNaN(cmdVal) ? KPLogic.CmdType.Request : KPLogic.CmdType.Standard) : KPLogic.CmdType.Binary; cmd.KPNum = kpNum; cmd.CmdNum = cmdNum; if (cmd.CmdType == KPLogic.CmdType.Standard) cmd.CmdVal = cmdVal; else if (cmd.CmdType == KPLogic.CmdType.Binary) cmd.CmdData = cmdData; return true; } else { cmd = null; return false; } }
/// <summary> /// Инициализировать канал связи /// </summary> /// <remarks>В случае исключения дальнейшая работа линии связи невозможна</remarks> public virtual void Init(SortedList <string, string> commCnlParams, List <KPLogic> kpList) { // проверка аргументов метода if (commCnlParams == null) { throw new ArgumentNullException("commCnlParams"); } if (kpList == null) { throw new ArgumentNullException("kpList"); } // копирование ссылок на КП линии связи foreach (KPLogic kpLogic in kpList) { if (kpLogic == null) { throw new ArgumentException("All the devices must not be null."); } this.kpList.Add(kpLogic); } kpListNotEmpty = kpList.Count > 0; firstKP = kpListNotEmpty ? kpList[0] : null; }
/// <summary> /// Цикл приёма данных по общему соединению в режиме ведомого (метод вызывается в отдельном потоке) /// </summary> protected void ListenSharedConn() { try { while (!terminated) { if (sharedTcpConn.TcpClient.Available > 0) { KPLogic targetKP = null; if (!ExecProcUnreadIncomingReq(firstKP, sharedTcpConn, ref targetKP)) { sharedTcpConn.ClearNetStream(inBuf); } } Thread.Sleep(SlaveThreadDelay); } } catch (Exception ex) { // данное исключение возникать не должно if (Localization.UseRussian) { WriteToLog("Ошибка при приёме данных по общему соединению: " + ex.Message); WriteToLog("Приём данных прекращён"); } else { WriteToLog("Error receiving data via shared connection: " + ex.Message); WriteToLog("Data receiving is terminated"); } } }
/// <summary> /// Обработать принятые по UDP данные /// </summary> protected void UdpReceiveCallback(IAsyncResult ar) { // приём данных byte[] buf; try { IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); buf = udpConn.UdpClient.EndReceive(ar, ref remoteEP); udpConn.RemoteAddress = remoteEP.Address.ToString(); udpConn.RemotePort = remoteEP.Port; WriteToLog(""); WriteToLog(string.Format(Localization.UseRussian ? "{0} Получены данные от {1}:{2}" : "{0} Data received from {1}:{2}", CommUtils.GetNowDT(), udpConn.RemoteAddress, udpConn.RemotePort)); if (buf == null) { WriteToLog(Localization.UseRussian ? "Данные пусты" : "Data is empty"); } } catch (Exception ex) { buf = null; WriteToLog(string.Format(Localization.UseRussian ? "Ошибка при приёме данных: {0}" : "Error receiving data: {0}", ex.Message)); } if (buf != null && kpListNotEmpty) { if (settings.DevSelMode == DeviceSelectionModes.ByIPAddress) { KPLogic kpLogic; if (kpCallNumDict.TryGetValue(udpConn.RemoteAddress, out kpLogic)) { // обработка входящего запроса для определённого КП ExecProcIncomingReq(kpLogic, buf, 0, buf.Length, ref kpLogic); } else { WriteToLog(string.Format(Localization.UseRussian ? "{0} Не удалось найти КП по IP-адресу {1}" : "{0} Unable to find device by IP address {1}", CommUtils.GetNowDT(), udpConn.RemoteAddress)); } } else // DeviceSelectionModes.ByDeviceLibrary { // обработка входящего запроса для произвольного КП KPLogic targetKP = null; ExecProcIncomingReq(firstKP, buf, 0, buf.Length, ref targetKP); } } StartUdpReceive(); }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public override void BeforeSession(KPLogic kpLogic) { // открытие последовательного порта, если он закрыт if (serialConn != null && !serialConn.Connected) { OpenSerialPort(); } }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public override void AfterSession(KPLogic kpLogic) { // закрытие последовательного порта, если произошла ошибка записи в порт if (serialConn != null && serialConn.Connected && serialConn.WriteError) { CloseSerialPort(); } }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public override void BeforeSession(KPLogic kpLogic) { if (udpConn != null && Behavior == OperatingBehaviors.Master) { udpConn.RemoteAddress = string.IsNullOrEmpty(kpLogic.CallNum) ? settings.RemoteIpAddress : kpLogic.CallNum; } }
/// <summary> /// Обработать событие приёма данных по последовательному порту /// </summary> protected void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { KPLogic targetKP = null; if (!ExecProcUnreadIncomingReq(kpList[0], serialConn, ref targetKP)) { serialConn.DiscardInBuffer(); } }
/// <summary> /// Конструктор /// </summary> public CommChannelLogic() { writeToLog = text => { }; // заглушка kpList = new List <KPLogic>(); kpListNotEmpty = false; firstKP = null; thread = null; terminated = false; }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public override void AfterSession(KPLogic kpLogic) { // очистка буфера неполностью считанной датаграммы, если сеанс опроса КП завершён с ошибкой if (kpLogic.WorkState == KPLogic.WorkStates.Error && Behavior == OperatingBehaviors.Master) { UdpConnection udpConn = kpLogic.Connection as UdpConnection; if (udpConn != null && udpConn.Connected) { udpConn.ClearDatagramBuffer(); } } }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public override void AfterSession(KPLogic kpLogic) { // очистка потока данных, если сеанс опроса КП завершён с ошибкой if (kpLogic.WorkState == KPLogic.WorkStates.Error && Behavior == OperatingBehaviors.Master) { TcpConnection tcpConn = kpLogic.Connection as TcpConnection; if (tcpConn != null && tcpConn.Connected) { tcpConn.ClearNetStream(inBuf); } } }
/// <summary> /// Обработать доступные данные в режиме соединения Shared /// </summary> protected void ProcAvailableDataShared(TcpConnection tcpConn) { // обработка входящего запроса в режиме ведомого для произвольного КП if (tcpConn == sharedTcpConn && slaveBehavior && firstKP != null) { KPLogic targetKP = null; if (!ExecProcUnreadIncomingReq(firstKP, tcpConn, ref targetKP)) { tcpConn.ClearNetStream(inBuf); } } }
/// <summary> /// Установить соединение для КП /// </summary> protected void SetConnection(KPLogic kpLogic, TcpConnection tcpConn) { TcpConnection existingTcpConn = kpLogic.Connection as TcpConnection; if (existingTcpConn != null) { existingTcpConn.Broken = true; existingTcpConn.ClearRelatedKPs(); } kpLogic.Connection = tcpConn; tcpConn.AddRelatedKP(kpLogic); }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public override void BeforeSession(KPLogic kpLogic) { // установка соединения при необходимости TcpConnection tcpConn = kpLogic.Connection as TcpConnection; if (tcpConn != null && !tcpConn.Connected) { string reason; if (tcpConn.CanOpen(out reason)) { try { // определение IP-адреса и TCP-порта string host; int port; if (tcpConn == sharedTcpConn) { host = settings.Host; port = settings.TcpPort; } else { CommUtils.ExtractHostAndPort(kpLogic.CallNum, settings.TcpPort, out host, out port); } // установка соединения WriteToLog(""); WriteToLog(string.Format(Localization.UseRussian ? "{0} Установка TCP-соединения с {1}:{2}" : "{0} Establish a TCP connection with {1}:{2}", CommUtils.GetNowDT(), host, port)); if (tcpConn.NetStream != null) // соединение уже было открыто, но разорвано { tcpConn.Renew(); } tcpConn.Open(host, port); } catch (Exception ex) { WriteToLog(ex.Message); } } else { WriteToLog(""); WriteToLog(string.Format("{0} {1}", CommUtils.GetNowDT(), reason)); } } }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public override void AfterSession(KPLogic kpLogic) { // разрыв соединения согласно настройкам, а также если сеанс опроса КП завершён с ошибкой if (!settings.StayConnected || kpLogic.WorkState == KPLogic.WorkStates.Error) { if (kpLogic.Connection is TcpConnection tcpConn && tcpConn.Connected) { WriteToLog(""); WriteToLog(string.Format(Localization.UseRussian ? "{0} Отключение от {1}" : "{0} Disconnect from {1}", CommUtils.GetNowDT(), tcpConn.RemoteAddress)); tcpConn.Disconnect(); } } }
/// <summary> /// Добавить КП, относящийся к данному соединению, в список /// </summary> public void AddRelatedKP(KPLogic kpLogic) { if (kpLogic == null) { throw new ArgumentNullException("kpLogic"); } if (relatedKPList == null) { relatedKPList = new List <KPLogic>(); } lock (relatedKPList) relatedKPList.Add(kpLogic); }
/// <summary> /// Выполнить метод ProcUnreadIncomingReq для заданного КП с обработкой исключений /// </summary> protected bool ExecProcUnreadIncomingReq(KPLogic kpLogic, Connection conn, ref KPLogic targetKP) { try { return(kpLogic.ProcUnreadIncomingReq(conn, ref targetKP)); } catch (Exception ex) { WriteToLog((Localization.UseRussian ? "Ошибка при обработке не считанного входящего запроса: " : "Error processing unread incoming request: ") + ex.Message); targetKP = null; return(false); } }
/// <summary> /// Выполнить метод ProcIncomingReq для заданного КП с обработкой исключений /// </summary> protected bool ExecProcIncomingReq(KPLogic kpLogic, byte[] buffer, int offset, int count, ref KPLogic targetKP) { try { return(kpLogic.ProcIncomingReq(buffer, offset, count, ref targetKP)); } catch (Exception ex) { WriteToLog((Localization.UseRussian ? "Ошибка при обработке считанного входящего запроса: " : "Error processing just read incoming request: ") + ex.Message); targetKP = null; return(false); } }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public override void AfterSession(KPLogic kpLogic) { // разрыв соединения, если сеанс опроса КП завершён с ошибкой if (kpLogic.WorkState == KPLogic.WorkStates.Error && Behavior == OperatingBehaviors.Master) { TcpConnection tcpConn = kpLogic.Connection as TcpConnection; if (tcpConn != null && tcpConn.Connected) { WriteToLog(""); WriteToLog(string.Format(Localization.UseRussian ? "{0} Отключение от {1}" : "{0} Disconnect from {1}", CommUtils.GetNowDT(), tcpConn.RemoteAddress)); tcpConn.Disconnect(); } } }
/// <summary> /// Обработать доступные данные в режиме соединения Individual /// </summary> protected void ProcAvailableDataIndiv(TcpConnection tcpConn) { if (tcpConn.RelatedKPExists) { // обработка входящего запроса в режиме ведомого для первого КП из группы с одинаковым позывным if (slaveBehavior) { KPLogic targetKP = tcpConn.GetFirstRelatedKP(); if (!ExecProcUnreadIncomingReq(targetKP, tcpConn, ref targetKP)) { tcpConn.ClearNetStream(inBuf); } } } else { // привязка соединения к КП по первому пакету данных if (devSelByFirstPackage) { if (tcpConn.JustConnected) { string firstPackage = ReceiveFirstPackage(tcpConn); if (!BindConnByFirstPackage(tcpConn, firstPackage)) { tcpConn.Broken = true; } } } else if (devSelByDeviceLibrary) { // привязка соединения к КП, используя произвольную библиотеку КП if (kpListNotEmpty) { KPLogic targetKP = null; if (!ExecProcUnreadIncomingReq(firstKP, tcpConn, ref targetKP)) { tcpConn.ClearNetStream(inBuf); } BindConnByDeviceLibrary(tcpConn, targetKP); } } } tcpConn.JustConnected = false; }
/// <summary> /// Привязать соединение к КП, используя библиотеку КП /// </summary> protected void BindConnByDeviceLibrary(TcpConnection tcpConn, KPLogic kpLogic) { if (kpLogic != null) { WriteToLog(string.Format(Localization.UseRussian ? "{0} Клиент {1} привязан к {2}, используя библиотеку КП" : "{0} The client {1} is bound to the {2} using a device library", CommUtils.GetNowDT(), tcpConn.RemoteAddress, kpLogic.Caption)); SetConnection(kpLogic, tcpConn); } else { WriteToLog(string.Format(Localization.UseRussian ? "{0} Не удалось привязать клиента {1} к КП, используя библиотеку КП" : "{0} Unable to bind the client {1} to a device using a device library", CommUtils.GetNowDT(), tcpConn.RemoteAddress)); } }
/// <summary> /// Цикл приёма данных по индивидуальным соединениям в режиме ведомого (метод вызывается в отдельном потоке) /// </summary> protected void ListenIndividualConn() { try { while (!terminated) { foreach (KPLogic kpLogic in kpList) { TcpConnection tcpConn = kpLogic.Connection as TcpConnection; if (tcpConn != null && tcpConn.TcpClient.Available > 0) { KPLogic targetKP = kpLogic; if (!ExecProcUnreadIncomingReq(kpLogic, tcpConn, ref targetKP)) { sharedTcpConn.ClearNetStream(inBuf); } } } Thread.Sleep(SlaveThreadDelay); } } catch (Exception ex) { // данное исключение возникать не должно if (Localization.UseRussian) { WriteToLog("Ошибка при приёме данных по индивидуальным соединениям: " + ex.Message); WriteToLog("Приём данных прекращён"); } else { WriteToLog("Error receiving data via individual connections: " + ex.Message); WriteToLog("Data receiving is terminated"); } } }
/// <summary> /// Listens to the serial port for incoming data. /// </summary> /// <remarks>This methods works in a separate thread. Is used in Mono.</remarks> protected void ListenSerialPort() { byte[] buffer = new byte[SlaveInBufLen]; int readCnt = 0; int prevReadCnt = 0; while (!terminated) { try { serialConn.SerialPort.ReadTimeout = 0; try { readCnt += serialConn.SerialPort.Read(buffer, readCnt, SlaveInBufLen - readCnt); } catch (TimeoutException) { } Thread.Sleep(ReadIntervalTimeout); if (prevReadCnt == readCnt && readCnt > 0 || readCnt == SlaveInBufLen) { KPLogic targetKP = null; if (!ExecProcIncomingReq(firstKP, buffer, 0, readCnt, ref targetKP)) { serialConn.DiscardInBuffer(); } readCnt = 0; } prevReadCnt = readCnt; } catch (Exception ex) { WriteToLog(string.Format(Localization.UseRussian ? "Ошибка при прослушивании последовательного порта: {0}" : "Error listening to the serial port: {0}", ex.Message)); } } }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public override void BeforeSession(KPLogic kpLogic) { // установка соединения при необходимости TcpConnection tcpConn = kpLogic.Connection as TcpConnection; if (tcpConn != null && !tcpConn.Connected) { try { // определение IP-адреса и TCP-порта IPAddress addr; int port; if (tcpConn == sharedTcpConn) { addr = IPAddress.Parse(settings.IpAddress); port = settings.TcpPort; } else { CommUtils.ExtractAddrAndPort(kpLogic.CallNum, settings.TcpPort, out addr, out port); } // установка соединения WriteToLog(""); WriteToLog(string.Format(Localization.UseRussian ? "{0} Установка TCP-соединения с {1}:{2}" : "{0} Establish a TCP connection with {1}:{2}", CommUtils.GetNowDT(), addr, port)); tcpConn.Open(addr, port); } catch (Exception ex) { WriteToLog(ex.Message); } } }
/// <summary> /// Передать команду КП /// </summary> public void PassCmd(KPLogic.Command cmd) { if (cmd != null && linesStarted) { try { lock (lineLock) { foreach (CommLine commLine in commLines) commLine.AddCmd(cmd); } } catch (Exception ex) { Log.WriteAction((Localization.UseRussian ? "Ошибка при передаче команды менеджеру: " : "Error passing command to the manager: ") + ex.Message, Log.ActTypes.Exception); } } }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public virtual void AfterSession(KPLogic kpLogic) { }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public virtual void BeforeSession(KPLogic kpLogic) { }
/// <summary> /// Отправить архивный срез SCADA-Серверу /// </summary> public bool SendArchive(KPLogic.ParamSrez arcSrez) { bool result; SrezTableLight.Srez srez = ConvertSrez(arcSrez); return srez == null || SendArchive(srez, out result) && result; }
/// <summary> /// Конструктор /// </summary> /// <param name="name">Наименование канала управления</param> /// <param name="cmdType">Тип команды</param> public CtrlCnlProps(string name, KPLogic.CmdType cmdType) { Name = name; CmdType = cmdType; CmdNum = 0; CmdValName = ""; FormulaUsed = false; Formula = ""; EvEnabled = false; }
/// <summary> /// Записать команду управления в файл /// </summary> /// <param name="cmdDir">Директория команд</param> /// <param name="sender">Имя приложения, отправившего команду</param> /// <param name="cmd">Команда КП</param> /// <param name="msg">Сообщение</param> /// <returns>Успешно ли произведена запись команды</returns> public static bool SaveCmd(string cmdDir, string sender, KPLogic.Command cmd, out string msg) { StringBuilder sbCmdData = new StringBuilder(); if (cmd.CmdData != null) { int cmdDataLen = cmd.CmdData.Length; for (int i = 0; i < cmdDataLen; i++) sbCmdData.Append(cmd.CmdData[i].ToString("X2")); } string[] cmdParams = new string[] { "KPNum=" + cmd.KPNum, "CmdNum=" + cmd.CmdNum, "CmdVal=" + cmd.CmdVal.ToString(), "CmdData=" + sbCmdData }; return SaveCmd(cmdDir, sender, cmd.CmdType.ToString(), cmdParams, out msg); }
/// <summary> /// Отправить событие SCADA-Серверу /// </summary> public bool SendEvent(KPLogic.Event aEvent) { if (aEvent == null || aEvent.KPParam == null || aEvent.KPParam.CnlNum <= 0) { return true; } else { EventTableLight.Event ev = new EventTableLight.Event(); ev.Number = aEvent.KPNum; ev.DateTime = aEvent.DateTime; ev.ObjNum = aEvent.KPParam.ObjNum; ev.KPNum = aEvent.KPNum; ev.ParamID = aEvent.KPParam.ParamID; ev.CnlNum = aEvent.KPParam.CnlNum; ev.OldCnlVal = aEvent.OldData.Val; ev.OldCnlStat = aEvent.OldData.Stat; ev.NewCnlVal = aEvent.NewData.Val; ev.NewCnlStat = aEvent.NewData.Stat; ev.Checked = aEvent.Checked; ev.UserID = aEvent.UserID; ev.Descr = aEvent.Descr; ev.Data = aEvent.Data; bool result; return SendEvent(ev, out result) && result; } }
/// <summary> /// Отправить текущий срез SCADA-Серверу /// </summary> public bool SendSrez(KPLogic.ParamSrez curSrez) { bool result; SrezTableLight.Srez srez = ConvertSrez(curSrez); return srez == null || SendSrez(srez, out result) && result; }
/// <summary> /// Преобразовать среза параметров в срез входных каналов /// </summary> private SrezTableLight.Srez ConvertSrez(KPLogic.ParamSrez paramSrez) { List<int> bindedIndexes; int cnlCnt; if (paramSrez == null) { bindedIndexes = null; cnlCnt = 0; } else { bindedIndexes = paramSrez.GetBindedParamIndexes(); cnlCnt = bindedIndexes.Count; } if (cnlCnt == 0) { return null; } else { SrezTableLight.Srez srez = new SrezTableLight.Srez(paramSrez.DateTime, cnlCnt); for (int i = 0; i < cnlCnt; i++) { int paramInd = bindedIndexes[i]; srez.CnlNums[i] = paramSrez.KPParams[paramInd].CnlNum; KPLogic.ParamData paramData = paramSrez.Data[paramInd]; SrezTableLight.CnlData cnlData = new SrezTableLight.CnlData(paramData.Val, paramData.Stat); srez.CnlData[i] = cnlData; } return srez; } }