/// <summary> /// Обработать уже считанный входящий запрос, относящийся к произвольному КП на линии связи /// </summary> /// <remarks>Если targetKP равен null, значит метод должен вернуть КП, которому адресован запрос. /// Возвращает true, если запрос успешно разобран. /// Метод выполняется в потоке канала связи</remarks> public virtual bool ProcIncomingReq(byte[] buffer, int offset, int count, ref KPLogic targetKP) { WriteToLog(""); WriteToLog(GetProcReqText(Connection, targetKP)); WriteToLog(Connection.BuildReadLogText(buffer, offset, count, Connection.DefaultLogFormat)); return(false); }
/// <summary> /// Получить текст для вывода в журнал при обработке входящего запроса /// </summary> private string GetProcReqText(Connection conn, KPLogic targetKP) { StringBuilder sb = new StringBuilder(DateTime.Now.ToString(CommUtils.CommLineDTFormat)); if (Localization.UseRussian) { sb.Append(" Обработка входящего запроса"); if (targetKP != null) { sb.Append(" от "); AppendKPDescr(sb); } if (!string.IsNullOrEmpty(conn.RemoteAddress)) { sb.Append(", удалённый адрес: ").Append(conn.RemoteAddress); } } else { sb.Append(" Process incoming request"); if (targetKP != null) { sb.Append(" from the "); AppendKPDescr(sb); } if (!string.IsNullOrEmpty(conn.RemoteAddress)) { sb.Append(", remote address: ").Append(conn.RemoteAddress); } } return(sb.ToString()); }
/// <summary> /// Преобразовать среза параметров в срез входных каналов /// </summary> private SrezTableLight.Srez ConvertSrez(KPLogic.TagSrez tagSrez) { List<int> boundIndexes; int cnlCnt; if (tagSrez == null) { boundIndexes = null; cnlCnt = 0; } else { boundIndexes = tagSrez.GetBoundTagIndexes(); cnlCnt = boundIndexes.Count; } if (cnlCnt == 0) { return null; } else { SrezTableLight.Srez srez = new SrezTableLight.Srez(tagSrez.DateTime, cnlCnt); for (int i = 0; i < cnlCnt; i++) { int tagInd = boundIndexes[i]; srez.CnlNums[i] = tagSrez.KPTags[tagInd].CnlNum; srez.CnlData[i] = tagSrez.TagData[tagInd]; } return srez; } }
/// <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 CommChannelLogic() { writeToLog = text => { }; // заглушка kpList = new List<KPLogic>(); kpListNotEmpty = false; firstKP = null; thread = null; terminated = false; }
/// <summary> /// Обработать не считанный входящий запрос, относящийся к произвольному КП на линии связи /// </summary> /// <remarks>Если targetKP равен null, значит метод должен вернуть КП, которому адресован запрос. /// Возвращает true, если запрос успешно считан и разобран. /// Метод выполняется в потоке канала связи</remarks> public virtual bool ProcUnreadIncomingReq(Connection conn, ref KPLogic targetKP) { WriteToLog(""); WriteToLog(GetProcReqText(conn, targetKP)); return false; }
/// <summary> /// Обработать уже считанный входящий запрос, относящийся к произвольному КП на линии связи /// </summary> /// <remarks>Если targetKP равен null, значит метод должен вернуть КП, которому адресован запрос. /// Возвращает true, если запрос успешно разобран. /// Метод выполняется в потоке канала связи</remarks> public virtual bool ProcIncomingReq(byte[] buffer, int offset, int count, ref KPLogic targetKP) { WriteToLog(""); WriteToLog(GetProcReqText(Connection, targetKP)); WriteToLog(Connection.BuildReadLogText(buffer, offset, count, Connection.DefaultLogFormat)); return false; }
/// <summary> /// Отправить архивный срез SCADA-Серверу /// </summary> public bool SendArchive(KPLogic.TagSrez arcSrez) { bool result; SrezTableLight.Srez srez = ConvertSrez(arcSrez); return srez == null || SendArchive(srez, out result) && result; }
/// <summary> /// Выполнить действия после сеанса опроса КП или отправки команды /// </summary> public virtual void AfterSession(KPLogic kpLogic) { }
/// <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 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 void AddRelatedKP(KPLogic kpLogic) { if (kpLogic == null) throw new ArgumentNullException("kpLogic"); if (relatedKPList == null) relatedKPList = new List<KPLogic>(); lock (relatedKPList) relatedKPList.Add(kpLogic); }
/// <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> /// Отправить событие SCADA-Серверу /// </summary> public bool SendEvent(KPLogic.KPEvent kpEvent) { if (kpEvent == null || kpEvent.KPTag == null || kpEvent.KPTag.CnlNum <= 0) { return true; } else { EventTableLight.Event ev = new EventTableLight.Event(); ev.Number = kpEvent.KPNum; ev.DateTime = kpEvent.DateTime; ev.ObjNum = kpEvent.KPTag.ObjNum; ev.KPNum = kpEvent.KPNum; ev.ParamID = kpEvent.KPTag.ParamID; ev.CnlNum = kpEvent.KPTag.CnlNum; ev.OldCnlVal = kpEvent.OldData.Val; ev.OldCnlStat = kpEvent.OldData.Stat; ev.NewCnlVal = kpEvent.NewData.Val; ev.NewCnlStat = kpEvent.NewData.Stat; ev.Checked = kpEvent.Checked; ev.UserID = kpEvent.UserID; ev.Descr = kpEvent.Descr; ev.Data = kpEvent.Data; bool result; return SendEvent(ev, out result) && result; } }
/// <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> /// <remarks>Если targetKP равен null, значит метод должен вернуть КП, которому адресован запрос. /// Возвращает true, если запрос успешно считан и разобран. /// Метод выполняется в потоке канала связи</remarks> public virtual bool ProcUnreadIncomingReq(Connection conn, ref KPLogic targetKP) { WriteToLog(""); WriteToLog(GetProcReqText(conn, targetKP)); return(false); }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public virtual void BeforeSession(KPLogic kpLogic) { }
/// <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 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)); if (tcpConn.NetStream != null) // соединение уже было открыто, но разорвано tcpConn.Renew(); tcpConn.Open(addr, port); } catch (Exception ex) { WriteToLog(ex.Message); } } }
/// <summary> /// Отправить текущий срез SCADA-Серверу /// </summary> public bool SendSrez(KPLogic.TagSrez curSrez) { bool result; SrezTableLight.Srez srez = ConvertSrez(curSrez); return srez == null || SendSrez(srez, out result) && result; }
/// <summary> /// Получить текст для вывода в журнал при обработке входящего запроса /// </summary> private string GetProcReqText(Connection conn, KPLogic targetKP) { StringBuilder sb = new StringBuilder(DateTime.Now.ToString(CommUtils.CommLineDTFormat)); if (Localization.UseRussian) { sb.Append(" Обработка входящего запроса"); if (targetKP != null) { sb.Append(" от "); AppendKPDescr(sb); } if (!string.IsNullOrEmpty(conn.RemoteAddress)) sb.Append(", удалённый адрес: ").Append(conn.RemoteAddress); } else { sb.Append(" Process incoming request"); if (targetKP != null) { sb.Append(" from the "); AppendKPDescr(sb); } if (!string.IsNullOrEmpty(conn.RemoteAddress)) sb.Append(", remote address: ").Append(conn.RemoteAddress); } return sb.ToString(); }
/// <summary> /// Выполнить действия перед сеансом опроса КП или отправкой команды /// </summary> public override void BeforeSession(KPLogic kpLogic) { if (udpConn != null && Behavior == OperatingBehaviors.Master) udpConn.RemoteAddress = string.IsNullOrEmpty(kpLogic.CallNum) ? settings.RemoteIpAddress : kpLogic.CallNum; }