/// <summary> /// Writes the slice of the current data. /// </summary> public void WriteCurrentData(DeviceSlice deviceSlice) { if (deviceSlice == null) { throw new ArgumentNullException(nameof(deviceSlice)); } if (varByDevice != null && varByDevice.TryGetValue(deviceSlice.DeviceNum, out DeviceVars deviceVars)) { lock (dataLock) { int dataIndex = 0; foreach (DeviceTag deviceTag in deviceSlice.DeviceTags) { if (deviceVars.TryGetValue(deviceTag.Code, out VarItem varItem)) { SetVariable(varItem, deviceSlice.CnlData, dataIndex, deviceSlice.Timestamp); } dataIndex += deviceTag.DataLength; } } } }
/// <summary> /// Converts the device slice to a general purpose slice. /// </summary> private bool ConvertSlice(DeviceSlice srcSlice, out Slice destSlice) { try { int srcDataLength = srcSlice.CnlData.Length; int destDataLength = 0; List <int> cnlNums = new List <int>(srcDataLength); foreach (DeviceTag deviceTag in srcSlice.DeviceTags) { if (deviceTag.InCnl != null) { cnlNums.Add(deviceTag.InCnl.CnlNum); destDataLength += deviceTag.DataLength; } } if (destDataLength == 0) { destSlice = null; return(false); } else if (destDataLength == srcDataLength) { destSlice = new Slice(srcSlice.Timestamp, cnlNums.ToArray(), srcSlice.CnlData); return(true); } else { CnlData[] destCnlData = new CnlData[destDataLength]; int dataIndex = 0; foreach (DeviceTag deviceTag in srcSlice.DeviceTags) { if (deviceTag.InCnl != null) { // TODO: bug, deviceTag.DataIndex is inapropriate here int tagDataLength = deviceTag.DataLength; Array.Copy(srcSlice.CnlData, deviceTag.DataIndex, destCnlData, dataIndex, tagDataLength); dataIndex += tagDataLength; } } destSlice = new Slice(srcSlice.Timestamp, cnlNums.ToArray(), destCnlData); return(true); } } catch (Exception ex) { log.WriteException(ex, CommPhrases.DataSourceMessage, Code, string.Format(Locale.IsRussian ? "Ошибка при конвертировании среза от устройства {0}" : "Error converting slice from the device {0}", srcSlice.DeviceNum)); destSlice = null; return(false); } }
/// <summary> /// Calls the FailedToSendCallback method of the device slice. /// </summary> private void CallFailedToSend(DeviceSlice deviceSlice) { try { deviceSlice.FailedToSendCallback?.Invoke(deviceSlice, Code); } catch (Exception ex) { log.WriteError(ex, CommPhrases.DataSourceMessage, Code, Locale.IsRussian ? "Ошибка при вызове метода среза FailedToSendCallback" : "Error calling the FailedToSendCallback method of the slice"); } }
/// <summary> /// Publishes the device slice. /// </summary> private bool PublishDeviceSlice(DeviceSlice deviceSlice) { try { if (topicByDevice.TryGetValue(deviceSlice.DeviceNum, out DeviceTopics deviceTopics)) { int dataIndex = 0; foreach (DeviceTag deviceTag in deviceSlice.DeviceTags) { if (!string.IsNullOrEmpty(deviceTag.Code) && deviceTopics.TryGetValue(deviceTag.Code, out string topic)) { string payloadStr = BuildPayload(deviceTag, deviceSlice.CnlData, dataIndex); MqttApplicationMessage message = new() { Topic = topic, Payload = Encoding.UTF8.GetBytes(payloadStr), QualityOfServiceLevel = (MqttQualityOfServiceLevel)dsOptions.PublishOptions.QosLevel, Retain = dsOptions.PublishOptions.Retain }; if (dsOptions.PublishOptions.DetailedLog) { dsLog.WriteAction("{0} {1} = {2}", CommPhrases.SendNotation, topic, payloadStr); } MqttClientPublishResult result = mqttClientHelper.Publish(message); if (result.ReasonCode != MqttClientPublishReasonCode.Success) { dsLog.WriteError(CommPhrases.ErrorPrefix + result.ReasonCode); return(false); } } dataIndex += deviceTag.DataLength; } } return(true); } catch (Exception ex) { dsLog.WriteError(ex, Locale.IsRussian ? "Ошибка при публикации данных" : "Error publishing data"); return(false); } }
/// <summary> /// Sends the telecontrol command. /// </summary> public override void SendCommand(TeleCommand cmd) { base.SendCommand(cmd); if (cmd.CmdCode == TagCode.DO || cmd.CmdNum == 4) { double relayVal = cmd.CmdVal > 0 ? 1 : 0; Log.WriteLine(Locale.IsRussian ? "Установить состояние реле в {0}" : "Set the relay state to {0}", relayVal); DeviceData.Set(TagCode.DO, relayVal); } else if (cmd.CmdCode == TagCode.AO || cmd.CmdNum == 5) { Log.WriteLine(Locale.IsRussian ? "Установить аналоговый выход в {0}" : "Set the analog output to {0}", cmd.CmdVal); DeviceData.Set(TagCode.AO, cmd.CmdVal); } else if (cmd.CmdCode == "Hist") { // demonstrate how to create a historical data slice DateTime now = DateTime.UtcNow; DeviceSlice deviceSlice = new DeviceSlice( new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc), 1, 1); deviceSlice.DeviceTags[0] = DeviceTags[TagCode.Sin]; deviceSlice.CnlData[0] = new CnlData(cmd.CmdVal, CnlStatusID.Defined); deviceSlice.Descr = "Demo slice"; DeviceData.EnqueueSlice(deviceSlice); } else if (cmd.CmdCode == "Event") { // demonstrate how to create an event DeviceData.EnqueueEvent(new DeviceEvent(DeviceTags[TagCode.Sin]) { Timestamp = DateTime.UtcNow, CnlVal = cmd.CmdVal, CnlStat = CnlStatusID.Defined, Descr = "Demo event" }); } else { LastRequestOK = false; Log.WriteLine(CommPhrases.InvalidCommand); } FinishCommand(); }
/// <summary> /// Calls the WriteHistoricalData method of the data sources. /// </summary> public void WriteHistoricalData(DeviceSlice deviceSlice) { foreach (DataSourceLogic dataSourceLogic in dataSources) { try { if (dataSourceLogic.IsReady) { dataSourceLogic.WriteHistoricalData(deviceSlice); } } catch (Exception ex) { log.WriteException(ex, CommPhrases.ErrorInDataSource, nameof(WriteHistoricalData), dataSourceLogic.Code); } } }
/// <summary> /// Writes the slice of the current data. /// </summary> public override void WriteCurrentData(DeviceSlice deviceSlice) { if (CheckDeviceFilter(deviceSlice.DeviceNum)) { lock (curDataQueue) { // remove current data from the beginning of the queue while (curDataQueue.Count >= maxQueueSize) { curDataQueue.Dequeue(); curDataSkipped++; } // append current data curDataQueue.Enqueue(new QueueItem <DeviceSlice>(deviceSlice.Timestamp, deviceSlice)); } } }
/// <summary> /// Writes the slice of the current data. /// </summary> public override void WriteCurrentData(DeviceSlice deviceSlice) { if (CheckDeviceFilter(deviceSlice.DeviceNum)) { lock (curDataQueue) { // remove data if capacity is not enough while (curDataQueue.Count >= maxQueueSize) { curDataQueue.Dequeue(); curDataSkipped++; } // append current data curDataQueue.Enqueue(new QueueItem <DeviceSlice>(deviceSlice.Timestamp, deviceSlice)); } } }
/// <summary> /// Transfers data from the device to the server. /// </summary> private void TransferDeviceData(DeviceLogic deviceLogic, bool allData) { // current data DeviceSlice currentSlice = deviceLogic.GetCurrentData(allData); if (!currentSlice.IsEmpty) { coreLogic.EnqueueCurrentData(currentSlice); } // historical data while (deviceLogic.DeviceData.DequeueSlice(out DeviceSlice historicalSlice)) { coreLogic.EnqueueHistoricalData(historicalSlice); } // events while (deviceLogic.DeviceData.DequeueEvent(out DeviceEvent deviceEvent)) { coreLogic.EnqueueEvent(deviceEvent); } }
/// <summary> /// Writes the slice of historical data. /// </summary> public override void WriteHistoricalData(DeviceSlice deviceSlice) { if (CheckDeviceFilter(deviceSlice.DeviceNum)) { lock (histDataQueue) { if (histDataQueue.Count < maxQueueSize) { histDataQueue.Enqueue(new QueueItem <DeviceSlice>(DateTime.UtcNow, deviceSlice)); } else { log.WriteError(CommPhrases.DataSourceMessage, Code, string.Format(Locale.IsRussian ? "Невозможно добавить исторические данные в очередь. Максимальный размер очереди {0} превышен" : "Unable to enqueue historical data. The maximum size of the queue {0} is exceeded", maxQueueSize)); histDataSkipped++; CallFailedToSend(deviceSlice); } } } }
/// <summary> /// Writes the slice of historical data. /// </summary> public virtual void WriteHistoricalData(DeviceSlice deviceSlice) { }
/// <summary> /// Writes the slice of the current data. /// </summary> public virtual void WriteCurrentData(DeviceSlice deviceSlice) { }
/// <summary> /// Converts the device slice to a general purpose slice. /// </summary> private bool ConvertSlice(DeviceSlice srcSlice, out Slice destSlice) { try { int srcDataLength = srcSlice.CnlData.Length; int destDataLength = 0; List <int> cnlNums = new List <int>(srcDataLength); foreach (DeviceTag deviceTag in srcSlice.DeviceTags) { if (deviceTag == null) { throw new ScadaException(Locale.IsRussian ? "Неопределенные теги в срезе не допускаются." : "Undefined tags are not allowed in a slice."); } if (deviceTag.Cnl != null) { int tagDataLength = deviceTag.DataLength; for (int i = 0; i < tagDataLength; i++) { cnlNums.Add(deviceTag.Cnl.CnlNum + i); } destDataLength += tagDataLength; } } if (destDataLength == 0) { destSlice = null; return(false); } else if (destDataLength == srcDataLength) { destSlice = new Slice(srcSlice.Timestamp, cnlNums.ToArray(), srcSlice.CnlData); return(true); } else { CnlData[] destCnlData = new CnlData[destDataLength]; int srcDataIndex = 0; int destDataIndex = 0; foreach (DeviceTag deviceTag in srcSlice.DeviceTags) { int tagDataLength = deviceTag.DataLength; if (deviceTag.Cnl != null) { Array.Copy(srcSlice.CnlData, srcDataIndex, destCnlData, destDataIndex, tagDataLength); destDataIndex += tagDataLength; } srcDataIndex += tagDataLength; } destSlice = new Slice(srcSlice.Timestamp, cnlNums.ToArray(), destCnlData); return(true); } } catch (Exception ex) { log.WriteError(ex, CommPhrases.DataSourceMessage, Code, string.Format(Locale.IsRussian ? "Ошибка при конвертировании среза от устройства {0}" : "Error converting slice from the device {0}", srcSlice.DeviceNum)); destSlice = null; return(false); } }
/// <summary> /// Writes the slice of the current data. /// </summary> public override void WriteCurrentData(DeviceSlice deviceSlice) { opcServer?.NodeManager?.WriteCurrentData(deviceSlice); }