public virtual void Reset() { this.receiveBootupHeartbeart = false; this.faultReason = null; this.actionQueue.Clear(); this.commState = CommStates.idle; this.consumerHeartbeatActive = false; this.consumerHeartbeatSetting = 0; NetworkRequest networkRequest = new NetworkRequest(0x81, this.NodeId); this.ScheduleAction(networkRequest); }
public virtual void Update() { CommStates processedState; do { processedState = this.commState; switch (this.commState) { case CommStates.idle: { lock (this) { if (this.actionQueue.Count != 0) { this.action = (CommAction)this.actionQueue.Dequeue(); } } if (null != this.action) { this.commAttemptCount = 0; this.commState = CommStates.tx; //Tracer.WriteHigh(TraceGroup.CANBUS, "", "action start"); } break; } case CommStates.tx: { this.commAttemptCount++; if (this.commAttemptCount <= this.action.RetryAttemptLimit) { int cobId = 0; byte[] frameData = this.action.GetTransmitFrame(ref cobId, this.NodeId); if (null != frameData) { bool txResult = this.Transmit(cobId, frameData); if (false != txResult) { this.action.TransmitComplete(); bool responseNeeded = this.action.ResponseNeeded(); if (false != responseNeeded) { //Tracer.WriteHigh(TraceGroup.CANBUS, "", "tx pending"); this.commTimeLimit = DateTime.Now.AddMilliseconds(this.action.RetryTime); this.commState = CommStates.rxWait; } else { this.EvaluateAction(this.action); this.action = null; this.commState = CommStates.idle; } } else { Tracer.WriteHigh(TraceGroup.CANBUS, "", "tx fail"); this.commTimeLimit = DateTime.Now.AddMilliseconds(this.TransmitFailureHoldoffTime); this.commState = CommStates.txWait; } } else { this.action = null; this.commState = CommStates.idle; } } else { this.Fault("comm failure"); this.action = null; } break; } case CommStates.txWait: { if (DateTime.Now > this.commTimeLimit) { this.commState = CommStates.tx; } break; } case CommStates.rxWait: { if (false != this.action.Done) { this.EvaluateAction(this.action); if (false != this.action.Aborted) { this.Fault("comm abort"); } this.action = null; this.commState = CommStates.idle; } else if (false != this.action.Transmit) { this.commAttemptCount = 0; this.commState = CommStates.tx; } else if (DateTime.Now > this.commTimeLimit) { this.commState = CommStates.tx; Tracer.WriteHigh(TraceGroup.CANBUS, "", "response timeout"); } break; } case CommStates.error: { break; } } } while (processedState != this.commState); if (null == this.FaultReason) { if ((false != this.consumerHeartbeatActive) && (DateTime.Now > this.consumerHeartbeatTimeLimit)) { this.Fault("heartbeat missing"); } } #region Download Process if (false != this.downloadActive) { string downloadResult = null; bool downloadComplete = false; if (null != this.FaultReason) { downloadComplete = true; downloadResult = "Device faulted: " + this.FaultReason; } else if (false != this.downloadCancel) { downloadComplete = true; downloadResult = "Cancelled"; } else if (DownloadSteps.start == this.downloadStep) { if (File.Exists(this.downloadFile) != false) { FileStream fs = File.Open(this.downloadFile, FileMode.Open); BinaryReader br = new BinaryReader(fs); this.downloadImagePosition = 0; this.downloadImageSize = (UInt32)fs.Length; this.downloadData = br.ReadBytes((int)this.downloadImageSize); br.Close(); br.Dispose(); fs.Close(); fs.Dispose(); this.receiveBootupHeartbeart = false; this.downloadAction = new NetworkRequest(0x80, this.NodeId); bool result = this.ScheduleAction(this.downloadAction); if (false != result) { this.downloadTimeLimit = DateTime.Now.AddMilliseconds(700); this.downloadStep = DownloadSteps.waitReset; } else { downloadResult = "Unable to schedule reset."; downloadComplete = true; } } else { downloadResult = "Unable to open file."; downloadComplete = true; } } else if (DownloadSteps.waitReset == this.downloadStep) { if ((null == this.downloadAction) && (false != this.receiveBootupHeartbeart)) { this.downloadTimeLimit = DateTime.Now.AddMilliseconds(500); this.downloadStep = DownloadSteps.waitBootStart; } else if (DateTime.Now > this.downloadTimeLimit) { this.downloadTimeLimit = DateTime.Now.AddMilliseconds(500); this.downloadStep = DownloadSteps.waitBootStart; } } else if (DownloadSteps.waitBootStart == this.downloadStep) { if (DateTime.Now > this.downloadTimeLimit) { this.downloadAction = new SDODownload(0x1F50, 1, this.downloadData, 0, this.downloadData.Length); bool result = this.ScheduleAction(this.downloadAction, 5000, 1); if (false != result) { this.downloadStep = DownloadSteps.waitFirmwareDownload; } else { downloadResult = "Unable to schedule download."; downloadComplete = true; } } } else if (DownloadSteps.waitFirmwareDownload == this.downloadStep) { if (null != this.downloadAction) { this.downloadImagePosition = ((SDODownload)this.downloadAction).SentCount; } else { this.downloadImagePosition = this.downloadImageSize; this.downloadAction = new SDODownload(0x1F51, 1, 1, 1); bool result = this.ScheduleAction(this.downloadAction); if (false != result) { this.downloadTimeLimit = DateTime.Now.AddSeconds(2); this.downloadStep = DownloadSteps.waitStartRequest; } else { downloadResult = "Unable to schedule start."; downloadComplete = true; } } } else if (DownloadSteps.waitStartRequest == this.downloadStep) { if (null == this.downloadAction) { downloadResult = null; downloadComplete = true; } } else { downloadResult = "Unknown step."; downloadComplete = true; } if (false != downloadComplete) { this.downloadData = null; this.downloadActive = false; this.downloadCompleteHandler(downloadResult); } } #endregion }
public virtual void Initialize() { this.faultReason = null; this.receiveBootupHeartbeart = false; this.consumerHeartbeatActive = false; this.consumerHeartbeatSetting = 0; this.actionQueue.Clear(); this.commState = CommStates.idle; this.TransmitFailureHoldoffTime = 50; }
/// <summary> /// Отправить команду ТУ SCADA-Серверу /// </summary> protected bool SendCommand(int userID, int ctrlCnl, double cmdVal, byte[] cmdData, int kpNum, out bool result) { Monitor.Enter(tcpLock); bool complete = false; result = false; errMsg = ""; try { if (RestoreConnection()) { WriteAction(Localization.UseRussian ? "Отправка команды ТУ SCADA-Серверу" : "Send telecommand to SCADA-Server", Log.ActTypes.Action); commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // отправка команды int cmdLen = double.IsNaN(cmdVal) ? cmdData == null ? 12 : 10 + cmdData.Length : 18; byte[] buf = new byte[cmdLen]; buf[0] = (byte)(cmdLen % 256); buf[1] = (byte)(cmdLen / 256); buf[2] = 0x06; buf[3] = (byte)(userID % 256); buf[4] = (byte)(userID / 256); buf[6] = (byte)(ctrlCnl % 256); buf[7] = (byte)(ctrlCnl / 256); if (!double.IsNaN(cmdVal)) // стандартная команда { buf[5] = 0x00; buf[8] = 0x08; buf[9] = 0x00; byte[] bytes = BitConverter.GetBytes(cmdVal); Array.Copy(bytes, 0, buf, 10, 8); } else if (cmdData != null) // бинарная команда { buf[5] = 0x01; int cmdDataLen = cmdData.Length; buf[8] = (byte)(cmdDataLen % 256); buf[9] = (byte)(cmdDataLen / 256); Array.Copy(cmdData, 0, buf, 10, cmdDataLen); } else // опрос КП { buf[5] = 0x02; buf[8] = 0x02; buf[9] = 0x00; buf[10] = (byte)(kpNum % 256); buf[11] = (byte)(kpNum / 256); } netStream.Write(buf, 0, cmdLen); // приём результата buf = new byte[4]; int bytesRead = netStream.Read(buf, 0, 4); // обработка полученных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x06)) { result = buf[3] > 0; commState = result ? CommStates.Authorized : CommStates.NotReady; complete = true; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на команду ТУ" : "Incorrect SCADA-Server response to telecommand"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при отправке команды ТУ SCADA-Серверу: " : "Error sending telecommand to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return complete; }
/// <summary> /// Запросить правильность имени и пароля пользователя от SCADA-Сервера, получить его роль. /// Возвращает успешность выполнения запроса /// </summary> public bool CheckUser(string login, string password, out int roleID) { Monitor.Enter(tcpLock); bool result = false; roleID = (int)Roles.Disabled; errMsg = ""; try { if (RestoreConnection()) { commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // запрос правильности имени и пароля пользователя, его роли byte userLen = login == null ? (byte)0 : (byte)login.Length; byte pwdLen = password == null ? (byte)0 : (byte)password.Length; byte[] buf = new byte[5 + userLen + pwdLen]; buf[0] = (byte)(buf.Length % 256); buf[1] = (byte)(buf.Length / 256); buf[2] = 0x01; buf[3] = userLen; if (userLen > 0) Array.Copy(Encoding.Default.GetBytes(login), 0, buf, 4, userLen); buf[4 + userLen] = pwdLen; if (pwdLen > 0) Array.Copy(Encoding.Default.GetBytes(password), 0, buf, 5 + userLen, pwdLen); netStream.Write(buf, 0, buf.Length); // приём результата buf = new byte[4]; int bytesRead = netStream.Read(buf, 0, 4); // обработка полученных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x01)) { roleID = buf[3]; result = true; commState = CommStates.Authorized; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос правильности имени и пароля" : "Incorrect SCADA-Server response to check user name and password request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при запросе правильности имени и пароля пользователя от SCADA-Сервера: " : "Error requesting check user name and password to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return result; }
/// <summary> /// Принять дату и время изменения файла от SCADA-Сервера. /// В случае отсутствия файла возвращается минимальная дата /// </summary> public DateTime ReceiveFileAge(Dirs dir, string fileName) { Monitor.Enter(tcpLock); DateTime result = DateTime.MinValue; string filePath = DirToString(dir) + fileName; errMsg = ""; try { if (RestoreConnection()) { #if DETAILED_LOG WriteAction(string.Format(Localization.UseRussian ? "Приём даты и времени изменения файла {0} от SCADA-Сервера" : "Receive date and time of file {0} modification from SCADA-Server", filePath), Log.ActTypes.Action); #endif commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // отправка запроса даты и времени изменения файла int cmdLen = 6 + fileName.Length; byte[] buf = new byte[cmdLen]; buf[0] = (byte)(cmdLen % 256); buf[1] = (byte)(cmdLen / 256); buf[2] = 0x0C; buf[3] = 0x01; buf[4] = (byte)dir; buf[5] = (byte)fileName.Length; Array.Copy(Encoding.Default.GetBytes(fileName), 0, buf, 6, fileName.Length); netStream.Write(buf, 0, cmdLen); // приём даты и времени изменения файла buf = new byte[12]; netStream.Read(buf, 0, 12); // обработка даты и времени изменения файла if (CheckDataFormat(buf, 0x0C)) { double dt = BitConverter.ToDouble(buf, 4); result = dt == 0.0 ? DateTime.MinValue : Arithmetic.DecodeDateTime(dt); commState = CommStates.Authorized; } else { errMsg = string.Format(Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос даты и времени изменения файла {0}" : "Incorrect SCADA-Server response to file modification date and time request", filePath); WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = string.Format(Localization.UseRussian ? "Ошибка при приёме даты и времени изменения файла {0} от SCADA-Сервера: " : "Error receiving date and time of file {0} modification from SCADA-Server: ", filePath) + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return result; }
/// <summary> /// Принять файл от SCADA-Сервера /// </summary> protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) { bool result = false; string filePath = DirToString(dir) + fileName; try { #if DETAILED_LOG WriteAction(string.Format(Localization.UseRussian ? "Приём файла {0} от SCADA-Сервера" : "Receive file {0} from SCADA-Server", filePath), Log.ActTypes.Action); #endif commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; const int dataSize = 10240; // размер запрашиваемых данных 10 кБ const byte dataSizeL = dataSize % 256; const byte dataSizeH = dataSize / 256; byte[] buf = new byte[6 + dataSize]; // буфер отправляемых и получаемых данных bool open = true; // выполняется открытие файла bool stop = false; // признак завершения приёма данных while (!stop) { if (open) { // отправка команды открытия файла и чтения данных byte fileNameLen = (byte)fileName.Length; int cmdLen = 7 + fileNameLen; buf[0] = (byte)(cmdLen % 256); buf[1] = (byte)(cmdLen / 256); buf[2] = 0x08; buf[3] = (byte)dir; buf[4] = fileNameLen; Array.Copy(Encoding.Default.GetBytes(fileName), 0, buf, 5, fileNameLen); buf[cmdLen - 2] = dataSizeL; buf[cmdLen - 1] = dataSizeH; netStream.Write(buf, 0, cmdLen); } else { // отправка команды чтения данных из файла buf[0] = 0x05; buf[1] = 0x00; buf[2] = 0x0A; buf[3] = dataSizeL; buf[4] = dataSizeH; netStream.Write(buf, 0, 5); } // приём результата открытия файла и считанных данных byte cmdNum = buf[2]; int headerLen = open ? 6 : 5; int bytesRead = netStream.Read(buf, 0, headerLen); int dataSizeRead = 0; // размер считанных из файла данных if (bytesRead == headerLen) { dataSizeRead = buf[headerLen - 2] + 256 * buf[headerLen - 1]; if (0 < dataSizeRead && dataSizeRead <= dataSize) bytesRead += ReadNetStream(buf, headerLen, dataSizeRead); } if (CheckDataFormat(buf, cmdNum, bytesRead) && bytesRead == dataSizeRead + headerLen) { if (open) { open = false; if (buf[3] > 0) // файл открыт { inStream.Write(buf, 6, dataSizeRead); commState = CommStates.Authorized; stop = dataSizeRead < dataSize; } else { errMsg = string.Format(Localization.UseRussian ? "SCADA-Серверу не удалось открыть файл {0}" : "SCADA-Server unable to open file {0}", filePath); WriteAction(errMsg, Log.ActTypes.Action); commState = CommStates.NotReady; stop = true; } } else { inStream.Write(buf, 5, dataSizeRead); commState = CommStates.Authorized; stop = dataSizeRead < dataSize; } } else { errMsg = string.Format(Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на команду открытия или чтения из файла {0}" : "Incorrect SCADA-Server response to open file or read from file {0} command ", filePath); WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; stop = true; } } // определение результата if (commState == CommStates.Authorized) { if (inStream.Length > 0) inStream.Position = 0; result = true; } } catch (Exception ex) { errMsg = string.Format(Localization.UseRussian ? "Ошибка при приёме файла {0} от SCADA-Сервера: " : "Error receiving file {0} from SCADA-Server: ", filePath) + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); } return result; }
/// <summary> /// Установить соединение со SCADA-Сервером и произвести авторизацию /// </summary> protected bool Connect() { try { commState = CommStates.Disconnected; WriteAction(string.Format(Localization.UseRussian ? "Установка соединения со SCADA-Сервером \"{0}\"" : "Connect to SCADA-Server \"{0}\"", commSettings.ServerHost), Log.ActTypes.Action); // определение IP-адреса, если он указан в конфигурации программы IPAddress ipAddress = null; try { ipAddress = IPAddress.Parse(commSettings.ServerHost); } catch { } // создание, настройка и попытка установки соединения tcpClient = new TcpClient(); tcpClient.NoDelay = true; // sends data immediately upon calling NetworkStream.Write tcpClient.ReceiveBufferSize = 16384; // 16 кБ tcpClient.SendBufferSize = 8192; // 8 кБ, размер по умолчанию tcpClient.SendTimeout = TcpSendTimeout; tcpClient.ReceiveTimeout = TcpReceiveTimeout; if (ipAddress == null) tcpClient.Connect(commSettings.ServerHost, commSettings.ServerPort); else tcpClient.Connect(ipAddress, commSettings.ServerPort); netStream = tcpClient.GetStream(); // получение версии SCADA-Сервера byte[] buf = new byte[5]; int bytesRead = netStream.Read(buf, 0, 5); // обработка считанных данных версии if (bytesRead == buf.Length && CheckDataFormat(buf, 0x00)) { commState = CommStates.Connected; serverVersion = buf[4] + "." + buf[3]; // запрос правильности имени и пароля пользователя, его роли byte userLen = (byte)commSettings.ServerUser.Length; byte pwdLen = (byte)commSettings.ServerPwd.Length; buf = new byte[5 + userLen + pwdLen]; buf[0] = (byte)(buf.Length % 256); buf[1] = (byte)(buf.Length / 256); buf[2] = 0x01; buf[3] = userLen; Array.Copy(Encoding.Default.GetBytes(commSettings.ServerUser), 0, buf, 4, userLen); buf[4 + userLen] = pwdLen; Array.Copy(Encoding.Default.GetBytes(commSettings.ServerPwd), 0, buf, 5 + userLen, pwdLen); netStream.Write(buf, 0, buf.Length); // приём результата buf = new byte[4]; bytesRead = netStream.Read(buf, 0, 4); // обработка считанных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x01)) { Roles role = (Roles)buf[3]; if (role == Roles.App) { commState = CommStates.Authorized; } else if (role < Roles.Err) { errMsg = Localization.UseRussian ? "Недостаточно прав для соединения со SCADA-Сервером" : "Insufficient rights to connect to SCADA-Server"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } else // role == Roles.Err { errMsg = Localization.UseRussian ? "Неверное имя пользователя или пароль" : "User name or password is incorrect"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос правильности имени и пароля" : "Incorrect SCADA-Server response to check user name and password request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос версии" : "Incorrect SCADA-Server response to version request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при установке соединения со SCADA-Сервером: " : "Error connecting to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); } // возврат результата if (commState == CommStates.Authorized) { return true; } else { Disconnect(); return false; } }
/// <summary> /// Разорвать соединение со SCADA-Сервером /// </summary> protected void Disconnect() { try { commState = CommStates.Disconnected; serverVersion = ""; if (tcpClient != null) { WriteAction(Localization.UseRussian ? "Разрыв соединения со SCADA-Сервером" : "Disconnect from SCADA-Server", Log.ActTypes.Action); if (netStream != null) { // очистка (для корректного разъединения) и закрытие потока данных TCP-клиента ClearNetStream(); netStream.Close(); netStream = null; } tcpClient.Close(); tcpClient = null; } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при разрыве соединения со SCADA-Сервером: " : "Error disconnecting from SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); } }
/// <summary> /// Отправить команду квитирования события SCADA-Серверу /// </summary> public bool CheckEvent(int userID, DateTime date, int evNum, out bool result) { Monitor.Enter(tcpLock); bool complete = false; result = false; errMsg = ""; try { if (RestoreConnection()) { WriteAction(Localization.UseRussian ? "Отправка команды квитирования события SCADA-Серверу" : "Send check event command to SCADA-Server", Log.ActTypes.Action); commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // отправка команды byte[] buf = new byte[10]; buf[0] = 0x0A; buf[1] = 0x00; buf[2] = 0x0E; buf[3] = (byte)(userID % 256); buf[4] = (byte)(userID / 256); buf[5] = (byte)(date.Year % 100); buf[6] = (byte)date.Month; buf[7] = (byte)date.Day; buf[8] = (byte)(evNum % 256); buf[9] = (byte)(evNum / 256); netStream.Write(buf, 0, 10); // приём результата buf = new byte[4]; int bytesRead = netStream.Read(buf, 0, 4); // обработка полученных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x0E)) { result = buf[3] > 0; commState = result ? CommStates.Authorized : CommStates.NotReady; complete = true; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на команду квитирования события" : "Incorrect SCADA-Server response to check event command"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при отправке команды квитирования события SCADA-Серверу: " : "Error sending check event command to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return complete; }
/// <summary> /// Конструктор /// </summary> protected ServerComm() { tcpClient = null; netStream = null; tcpLock = new object(); commState = CommStates.Disconnected; serverVersion = ""; restConnSuccDT = DateTime.MinValue; restConnErrDT = DateTime.MinValue; errMsg = ""; }
/// <summary> /// Отправить событие SCADA-Серверу /// </summary> public bool SendEvent(EventTableLight.Event aEvent, out bool result) { Monitor.Enter(tcpLock); bool complete = false; result = false; errMsg = ""; try { if (RestoreConnection()) { commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // отправка команды записи события byte descrLen = (byte)aEvent.Descr.Length; byte dataLen = (byte)aEvent.Data.Length; int cmdLen = 46 + descrLen + dataLen; byte[] buf = new byte[cmdLen]; buf[0] = (byte)(cmdLen % 256); buf[1] = (byte)(cmdLen / 256); buf[2] = 0x05; double evDT = Arithmetic.EncodeDateTime(aEvent.DateTime); byte[] bytes = BitConverter.GetBytes(evDT); Array.Copy(bytes, 0, buf, 3, 8); buf[11] = (byte)(aEvent.ObjNum % 256); buf[12] = (byte)(aEvent.ObjNum / 256); buf[13] = (byte)(aEvent.KPNum % 256); buf[14] = (byte)(aEvent.KPNum / 256); buf[15] = (byte)(aEvent.ParamID % 256); buf[16] = (byte)(aEvent.ParamID / 256); bytes = BitConverter.GetBytes(aEvent.CnlNum); Array.Copy(bytes, 0, buf, 17, 4); bytes = BitConverter.GetBytes(aEvent.OldCnlVal); Array.Copy(bytes, 0, buf, 21, 8); bytes = BitConverter.GetBytes(aEvent.OldCnlStat); Array.Copy(bytes, 0, buf, 29, 2); bytes = BitConverter.GetBytes(aEvent.NewCnlVal); Array.Copy(bytes, 0, buf, 31, 8); bytes = BitConverter.GetBytes(aEvent.NewCnlStat); Array.Copy(bytes, 0, buf, 39, 2); buf[41] = aEvent.Checked ? (byte)0x01 : (byte)0x00; buf[42] = (byte)(aEvent.UserID % 256); buf[43] = (byte)(aEvent.UserID / 256); buf[44] = descrLen; Array.Copy(Encoding.Default.GetBytes(aEvent.Descr), 0, buf, 45, descrLen); buf[45 + descrLen] = dataLen; Array.Copy(Encoding.Default.GetBytes(aEvent.Data), 0, buf, 46 + descrLen, dataLen); netStream.Write(buf, 0, cmdLen); // приём результата buf = new byte[4]; int bytesRead = netStream.Read(buf, 0, 4); // обработка полученных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x05)) { result = buf[3] > 0; commState = result ? CommStates.Authorized : CommStates.NotReady; complete = true; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на команду отправки события" : "Incorrect SCADA-Server response to sending event command"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при отправке события SCADA-Серверу: " : "Error sending event to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return complete; }
/// <summary> /// Отправить архивный срез SCADA-Серверу /// </summary> public bool SendArchive(SrezTableLight.Srez arcSrez, out bool result) { Monitor.Enter(tcpLock); bool complete = false; result = false; errMsg = ""; try { if (RestoreConnection()) { commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // отправка команды записи архивного среза int cnlCnt = arcSrez.CnlNums.Length; int cmdLen = cnlCnt * 14 + 13; byte[] buf = new byte[cmdLen]; buf[0] = (byte)(cmdLen % 256); buf[1] = (byte)(cmdLen / 256); buf[2] = 0x04; double arcDT = Arithmetic.EncodeDateTime(arcSrez.DateTime); byte[] bytes = BitConverter.GetBytes(arcDT); Array.Copy(bytes, 0, buf, 3, 8); buf[11] = (byte)(cnlCnt % 256); buf[12] = (byte)(cnlCnt / 256); for (int i = 0; i < cnlCnt; i++) { bytes = BitConverter.GetBytes((UInt32)arcSrez.CnlNums[i]); Array.Copy(bytes, 0, buf, i * 14 + 13, 4); SrezTableLight.CnlData data = arcSrez.CnlData[i]; bytes = BitConverter.GetBytes(data.Val); Array.Copy(bytes, 0, buf, i * 14 + 17, 8); bytes = BitConverter.GetBytes((UInt16)data.Stat); Array.Copy(bytes, 0, buf, i * 14 + 25, 2); } netStream.Write(buf, 0, cmdLen); // приём результата buf = new byte[4]; int bytesRead = netStream.Read(buf, 0, 4); // обработка полученных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x04)) { result = buf[3] > 0; commState = result ? CommStates.Authorized : CommStates.NotReady; complete = true; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на команду отправки архивного среза" : "Incorrect SCADA-Server response to sending archive data command"; WriteAction(errMsg, Log.ActTypes.Exception); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при отправке архивного среза SCADA-Серверу: " : "Error sending archive data to SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return complete; }
/// <summary> /// Принять команду ТУ от SCADA-Сервера /// </summary> /// <remarks> /// Для стандартной команды возвращаемые данные команды равны null. /// Для бинарной команды возвращаемое значение команды равно double.NaN. /// Для команды опроса КП возвращаемое значение команды равно double.NaN и данные команды равны null.</remarks> public bool ReceiveCommand(out int kpNum, out int cmdNum, out double cmdVal, out byte[] cmdData) { Monitor.Enter(tcpLock); bool result = false; kpNum = 0; cmdNum = 0; cmdVal = double.NaN; cmdData = null; errMsg = ""; try { if (RestoreConnection()) { commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; // запрос команды byte[] buf = new byte[3]; buf[0] = 0x03; buf[1] = 0x00; buf[2] = 0x07; netStream.Write(buf, 0, 3); // приём команды buf = new byte[5]; int bytesRead = netStream.Read(buf, 0, 5); int cmdDataLen = 0; if (bytesRead == 5) { cmdDataLen = buf[3] + buf[4] * 256; if (cmdDataLen > 0) { Array.Resize<byte>(ref buf, 10 + cmdDataLen); bytesRead += netStream.Read(buf, 5, 5 + cmdDataLen); } } // обработка полученных данных if (CheckDataFormat(buf, 0x07) && bytesRead == buf.Length) { if (cmdDataLen > 0) { byte cmdType = buf[5]; if (cmdType == 0) { cmdVal = BitConverter.ToDouble(buf, 10); } else if (cmdType == 1) { cmdData = new byte[cmdDataLen]; Array.Copy(buf, 10, cmdData, 0, cmdDataLen); } kpNum = buf[6] + buf[7] * 256; cmdNum = buf[8] + buf[9] * 256; commState = CommStates.Authorized; result = true; } else // команд в очереди нет { commState = CommStates.Authorized; } } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос команды ТУ" : "Incorrect SCADA-Server response to telecommand request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при приёме команды ТУ от SCADA-Сервера: " : "Error requesting telecommand from SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return result; }
public virtual void Fault(string faultReason) { this.faultReason = faultReason; this.commState = CommStates.error; this.actionQueue.Clear(); this.SignalCommError(); this.Stop(); }
/// <summary> /// Восстановить соединение со SCADA-Сервером и произвести авторизацию при необходимости /// </summary> protected bool RestoreConnection() { bool connectNeeded = false; // требуется повторное соединение DateTime now = DateTime.Now; if (commState >= CommStates.Authorized) { if (now - restConnSuccDT > PingSpan) { // проверка соединения try { WriteAction(Localization.UseRussian ? "Запрос состояния SCADA-Сервера" : "Request SCADA-Server state", Log.ActTypes.Action); commState = CommStates.WaitResponse; // запрос состояния SCADA-Сервера (ping) byte[] buf = new byte[3]; buf[0] = 0x03; buf[1] = 0x00; buf[2] = 0x02; netStream.Write(buf, 0, 3); // приём результата buf = new byte[4]; netStream.Read(buf, 0, 4); // обработка результата if (CheckDataFormat(buf, 0x02)) { commState = buf[3] > 0 ? CommStates.Authorized : CommStates.NotReady; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос состояния" : "Incorrect SCADA-Server response to state request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; connectNeeded = true; } } catch { connectNeeded = true; } } } else if (now - restConnErrDT > ConnectSpan) { connectNeeded = true; } // соединение при необходимости if (connectNeeded) { if (tcpClient != null) Disconnect(); if (Connect()) { restConnSuccDT = now; restConnErrDT = DateTime.MinValue; return true; } else { restConnSuccDT = DateTime.MinValue; restConnErrDT = now; return false; } } else { ClearNetStream(); // очистка потока данных TCP-клиента if (commState >= CommStates.Authorized) { restConnSuccDT = now; return true; } else { errMsg = Localization.UseRussian ? "Невозможно соединиться со SCADA-Сервером. Повторите попытку." : "Unable to connect to SCADA-Server. Try again."; return false; } } }
public virtual void Update() { CommStates processedState; do { processedState = this.commState; switch (this.commState) { case CommStates.idle: { lock (this) { if (this.actionQueue.Count != 0) { this.action = (CommAction)this.actionQueue.Dequeue(); } } if (null != this.action) { this.commAttemptCount = 0; this.commState = CommStates.tx; //Tracer.WriteHigh(TraceGroup.CANBUS, "", "action start"); } break; } case CommStates.tx: { this.commAttemptCount++; if (this.commAttemptCount <= this.action.RetryAttemptLimit) { int cobId = 0; byte[] frameData = this.action.GetTransmitFrame(ref cobId, this.NodeId); if (null != frameData) { bool txResult = this.Transmit(cobId, frameData); if (false != txResult) { this.action.TransmitComplete(); bool responseNeeded = this.action.ResponseNeeded(); if (false != responseNeeded) { //Tracer.WriteHigh(TraceGroup.CANBUS, "", "tx pending"); this.commTimeLimit = DateTime.Now.AddMilliseconds(this.action.RetryTime); this.commState = CommStates.rxWait; } else { this.EvaluateAction(this.action); this.action = null; this.commState = CommStates.idle; } } else { Tracer.WriteHigh(TraceGroup.CANBUS, "", "tx fail"); this.commTimeLimit = DateTime.Now.AddMilliseconds(this.TransmitFailureHoldoffTime); this.commState = CommStates.txWait; } } else { this.action = null; this.commState = CommStates.idle; } } else { this.Fault("comm failure"); this.action = null; } break; } case CommStates.txWait: { if (DateTime.Now > this.commTimeLimit) { this.commState = CommStates.tx; } break; } case CommStates.rxWait: { if (false != this.action.Done) { this.EvaluateAction(this.action); if (false != this.action.Aborted) { this.Fault("comm abort"); } this.action = null; this.commState = CommStates.idle; } else if (false != this.action.Transmit) { this.commAttemptCount = 0; this.commState = CommStates.tx; } else if (DateTime.Now > this.commTimeLimit) { this.commState = CommStates.tx; Tracer.WriteHigh(TraceGroup.CANBUS, "", "response timeout"); } break; } case CommStates.error: { break; } } } while (processedState != this.commState); if (null == this.FaultReason) { if ((false != this.consumerHeartbeatActive) && (DateTime.Now > this.consumerHeartbeatTimeLimit)) { this.Fault("heartbeat missing"); } } }
/// <summary> /// Принять тренд входного канала от SCADA-Сервера /// </summary> public bool ReceiveTrend(string tableName, DateTime date, Trend trend) { Monitor.Enter(tcpLock); bool result = false; errMsg = ""; try { try { if (RestoreConnection()) { WriteAction(string.Format(Localization.UseRussian ? "Приём тренда входного канала {0} от SCADA-Сервера. Файл: {1}" : "Receive input channel {0} trend from SCADA-Server. File: {1}", trend.CnlNum, tableName), Log.ActTypes.Action); commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; byte tableType; // тип таблицы: текущая, часовая или минутная byte year, month, day; // дата запрашиваемых данных if (tableName == "current.dat") { tableType = (byte)0x01; year = month = day = 0; } else { tableType = tableName.Length > 0 && tableName[0] == 'h' ? (byte)0x02 : (byte)0x03; year = (byte)(date.Year % 100); month = (byte)date.Month; day = (byte)date.Day; } // отправка запроса тренда входного канала byte[] buf = new byte[13]; buf[0] = 0x0D; buf[1] = 0x00; buf[2] = 0x0D; buf[3] = tableType; buf[4] = year; buf[5] = month; buf[6] = day; buf[7] = 0x01; buf[8] = 0x00; byte[] bytes = BitConverter.GetBytes(trend.CnlNum); Array.Copy(bytes, 0, buf, 9, 4); netStream.Write(buf, 0, 13); // приём данных тренда входного канала buf = new byte[7]; int bytesRead = netStream.Read(buf, 0, 7); int pointCnt = 0; if (bytesRead == 7) { pointCnt = buf[5] + buf[6] * 256; if (pointCnt > 0) { Array.Resize<byte>(ref buf, 7 + pointCnt * 18); bytesRead += ReadNetStream(buf, 7, buf.Length - 7); } } // заполение тренда входного канала из полученных данных if (bytesRead == buf.Length && buf[4] == 0x0D) { for (int i = 0; i < pointCnt; i++) { Trend.Point point; int pos = i * 18 + 7; point.DateTime = Arithmetic.DecodeDateTime(BitConverter.ToDouble(buf, pos)); point.Val = BitConverter.ToDouble(buf, pos + 8); point.Stat = BitConverter.ToUInt16(buf, pos + 16); trend.Points.Add(point); } trend.Sort(); result = true; commState = CommStates.Authorized; } else { errMsg = Localization.UseRussian ? "Неверный формат ответа SCADA-Сервера на запрос тренда входного канала" : "Incorrect SCADA-Server response to input channel trend request"; WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } } } finally { // очистка тренда, если не удалось получить новые данные if (!result) { trend.Clear(); trend.TableName = tableName; } } } catch (Exception ex) { errMsg = (Localization.UseRussian ? "Ошибка при приёме тренда входного канала от SCADA-Сервера: " : "Error receiving input channel trend from SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); Disconnect(); } finally { RestoreReceiveTimeout(); Monitor.Exit(tcpLock); } return result; }