/// <summary> /// Creates the set request file for edl40 mode. /// </summary> /// <param name="portId">The port id.</param> /// <param name="operationMode">The operation mode.</param> /// <returns></returns> public static SmlFile CreateSetRequestFileForOperationMode(string portId, BooleanParam operationMode) { var handler = new SmlHandler(portId); var smlFile = new SmlFile(); handler.AddOpenRequest(smlFile); handler.AddSetBooleanParamRequest( smlFile, operationMode); handler.AddCloseRequest(smlFile); return smlFile; }
/// <summary> /// Creates the get request file. /// </summary> /// <param name="portId">The port id.</param> /// <param name="obisCode">The obis code.</param> /// <returns></returns> public static SmlFile CreateGetRequestFile(string portId, long obisCode) { var handler = new SmlHandler(portId); var smlFile = new SmlFile(); handler.AddOpenRequest(smlFile); if (obisCode == (long)ObisId.EdlEventLog) { handler.AddGetProfileRequest(smlFile, (ObisId)obisCode); } else { handler.AddGetParamRequest(smlFile, (ObisId)obisCode); } handler.AddCloseRequest(smlFile); return smlFile; }
/// <summary> /// Adds the close request. /// </summary> /// <param name="reqFile">The req file.</param> private void AddCloseRequest(SmlFile reqFile) { var msg = new Core.Sml.Messages.Message() { TransactionId = new[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, CloseRequest = new Core.Sml.Messages.CloseRequest() }; //var msg = SmlMessageFactory.CloseReq(); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); }
/// <summary> /// Adds the set date time param request. /// </summary> /// <param name="reqFile">The req file.</param> /// <param name="param">The param.</param> private void AddSetDateTimeParamRequest(SmlFile reqFile, DateTimeParam param) { var timeToSet = param.Data.Value; var localOffset = (short)TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes; var seasonTimeOffset = timeToSet.IsDaylightSavingTime() ? (short)60 : (short)0; var msg = new Core.Sml.Messages.Message() { TransactionId = new[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, SetProcParameterRequest = new Core.Sml.Messages.SetProcParameterRequest() { TreePath = new List<Core.Obis.ObisId>() { new Core.Obis.ObisId((ulong)param.ObisCode) }, Tree = new Tree(new Core.Obis.ObisId((ulong)param.ObisCode), new Time(timeToSet.ToUniversalTime()) { Type = TimeType.LocalTimestamp, LocalOffset = localOffset, SeasonTimeOffset = seasonTimeOffset }) } }; //var obisId = (ObisId)param.ObisCode; //var paramTreePath = new SmlTreePath(obisId); //var tree = new SmlTree { ParameterName = new SmlOctetstring(ObisUtil.GetBytes(obisId)) }; //if (param.Data.HasValue == false) //{ // throw new ArgumentNullException("param"); //} //var smlTime = new SmlTime { Type = SmlTime.Choice.LocalTimestamp, UtcTime = param.Data.Value }; //tree.ParameterValue = new SmlProcParValue(smlTime); //var msg = SmlMessageFactory.SetProcParamReq(paramTreePath, tree); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); }
/// <summary> /// Adds the set unsigned param request. /// </summary> /// <param name="reqFile">The req file.</param> /// <param name="param">The param.</param> private void AddSetUnsignedParamRequest(SmlFile reqFile, UnsignedParam param) { var msg = new Core.Sml.Messages.Message() { TransactionId = new[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, SetProcParameterRequest = new Core.Sml.Messages.SetProcParameterRequest() { TreePath = new List<Core.Obis.ObisId>() { new Core.Obis.ObisId((ulong)param.ObisCode) }, Tree = new Tree(new Core.Obis.ObisId((ulong)param.ObisCode), param.Data.Value) } }; //var obisId = (ObisId)param.ObisCode; //var paramTreePath = new SmlTreePath(obisId); //var tree = new SmlTree { ParameterName = new SmlOctetstring(ObisUtil.GetBytes(obisId)) }; //if (param.Data.HasValue == false) //{ // throw new ArgumentNullException("param"); //} //// TODO: check if casting is obsolete //if (obisId == ObisId.EdlActiveTariffAplus || obisId == ObisId.EdlActiveTariffAminus) //{ // tree.ParameterValue = new SmlProcParValue(new SmlUnsigned8((byte)param.Data.Value)); //} //else //{ // tree.ParameterValue = new SmlProcParValue(new SmlUnsigned64(param.Data.Value)); //} //var msg = SmlMessageFactory.SetProcParamReq(paramTreePath, tree); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); }
/// <summary> /// Adds the set integer param request. /// </summary> /// <param name="reqFile">The req file.</param> /// <param name="param">The param.</param> private void AddSetIntegerParamRequest(SmlFile reqFile, IntegerParam param) { var msg = new Core.Sml.Messages.Message() { TransactionId = new[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, SetProcParameterRequest = new Core.Sml.Messages.SetProcParameterRequest() { TreePath = new List<Core.Obis.ObisId>() { new Core.Obis.ObisId((ulong)param.ObisCode) }, Tree = new Tree(new Core.Obis.ObisId((ulong)param.ObisCode), param.Data.Value) } }; //var obisId = (ObisId)param.ObisCode; //var paramTreePath = new SmlTreePath(obisId); //var tree = new SmlTree { ParameterName = new SmlOctetstring(ObisUtil.GetBytes(obisId)) }; //if (param.Data.HasValue == false) //{ // throw new ArgumentNullException("param"); //} //tree.ParameterValue = new SmlProcParValue(new SmlInteger64(param.Data.Value)); //var msg = SmlMessageFactory.SetProcParamReq(paramTreePath, tree); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); }
/// <summary> /// Adds the get profile request. /// </summary> /// <param name="reqFile">The req file.</param> /// <param name="obisId">The obis id.</param> private void AddGetProfileRequest(SmlFile reqFile, ObisId obisId) { var msg = new Core.Sml.Messages.Message() { TransactionId = new[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, GetProfileListRequest = new Core.Sml.Messages.GetProfileListRequest() { TreePath = new List<Core.Obis.ObisId>() { Core.Obis.ObisId.Parse(ObisUtil.GetBytes(obisId)) } } }; //var paramTreePath = new SmlTreePath(obisId); //var msg = SmlMessageFactory.GetProfileListReq(paramTreePath); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); }
/// <summary> /// Sends the file. /// </summary> /// <param name="requestFile">The SML file.</param> /// <param name="isManufacturerMode">if set to <c>true</c> [is manufacturer mode].</param> /// <returns> /// True if file could be written down the serial interface. /// </returns> private async Task<object> SendFile(SmlFile requestFile, bool isManufacturerMode = false) { if (requestFile == null) { throw new ArgumentNullException(nameof(requestFile)); } Core.Obis.ObisId reqObis; if (requestFile[1].GetProcParameterRequest != null) { reqObis = requestFile[1].GetProcParameterRequest.TreePath.First(); logger.Warn("READ OBIS: {0}", reqObis.ToHexString()); } else if (requestFile[1].SetProcParameterRequest != null) { reqObis = requestFile[1].SetProcParameterRequest.TreePath.First(); logger.Warn("WRITE OBIS: {0}", reqObis.ToHexString()); } else { throw new InvalidOperationException(); } var sendBuff = new List<byte>(); requestFile.Serialize(sendBuff); this.logger.Debug("Sending SML file of size {0}.", sendBuff.Count); using (await this.asyncLock.LockAsync()) { // try to read response from meter try { this.EnsureConnected(); // WriteAsync does not work with Advantech boxes //await this.tcpPort.GetStream().Write(sendBuff.ToArray(), 0, sendBuff.Count); //.WriteAsync(sendBuff.ToArray(), 0, sendBuff.Count, token.Token); this.logger.Trace("TX: {0}", BitConverter.ToString(sendBuff.ToArray())); this.logger.Debug("File sent, waiting for response from meter."); var receivedFile = await this.ReceiveSmlFile(TimeSpan.FromSeconds(8)); // process received file var result = this.GetMeterResponse(requestFile, receivedFile); return result; } catch (Exception ex) { this.logger.ErrorException("ReceiveSmlFile failed: " + ex.StackTrace, ex); this.tcpPort.Close(); throw; } } }
/// <summary> /// Creates the set request file. /// </summary> /// <param name="portId">The port id.</param> /// <param name="param">The param.</param> /// <returns></returns> public static SmlFile CreateSetRequestFile(string portId, DateTimeParam param) { var handler = new SmlHandler(portId); var smlFile = new SmlFile(); handler.AddOpenRequest(smlFile); handler.AddSetDateTimeParamRequest(smlFile, param); handler.AddCloseRequest(smlFile); return smlFile; }
/// <summary> /// Sends the request file. /// </summary> /// <param name="requestFile">The request file.</param> /// <param name="enableManufacturerMode">if set to <c>true</c> [enable manufacturer mode].</param> private async Task SendRequestFile(SmlFile requestFile, bool enableManufacturerMode) { logger.Info("Sending request file"); var response = await SendFile(requestFile, enableManufacturerMode).ConfigureAwait(false); var attentionRes = response as AttentionResponse; if (attentionRes == null) { logger.Error("No response received."); throw new DataException("No response received."); } if (attentionRes.AttentionNo != (ulong)AttentionCode.Ok) { logger.Error("Device returned an error: {0}.", attentionRes.AttentionNo); throw new DataException(string.Format("Device returned an error: {0}.", attentionRes.AttentionNo)); } }
/// <summary> /// Processes the periodic response. /// </summary> /// <param name="periodicFile">The periodic file.</param> /// <param name="response">The response.</param> private void ProcessPeriodicResponse(SmlFile periodicFile, object response) { Trace.WriteLine("Processing periodic response for com port {0}.", portName); try { if (periodicFile.Count < 2) { return; } for (int i = 0; i < periodicFile.Count; i++) { var getParamReq = periodicFile[i].GetProcParameterRequest; if (getParamReq == null) { continue; } if (getParamReq.TreePath.Count < 1) { continue; } var requestedObis = getParamReq.TreePath[0]; switch (requestedObis) { case (long)ObisId.EdlDzgNumberOfTariffs: var numberOfTariffs = Convert.ToInt32(response); SmlComService.GetConfiguredPort(portName).EdlMeterDevice.TariffCount = numberOfTariffs; Trace.WriteLine(string.Format("{0} Response for tariff count received", portName)); logger.Info("Response for read tariff count: {0}.", SmlComService.GetConfiguredPort(portName).EdlMeterDevice.TariffCount); TariffCountReadAt = DateTime.Now; break; case (long)ObisId.DeviceIdentificationParams: if (response is Tree) { Trace.WriteLine(string.Format("{0} Response for device id received", portName)); var di = CreateEdlDeviceIdentification((Tree)response); var md = SmlComService.GetConfiguredPort(portName).EdlMeterDevice; if (md == null) { throw new ApplicationException(string.Format("Meter device for port {0} is null.", portName)); } SmlComService.GetConfiguredPort(portName).EdlMeterDevice.PublicKey = di.PublicKey.Data; SmlComService.GetConfiguredPort(portName).EdlMeterDevice.ServerId = di.ServerId.Data; try { SmlComService.GetConfiguredPort(portName).EdlMeterDevice.FirmwareVersion = di.FirmwareVersion.Data; } catch (Exception ex) { logger.ErrorException( string.Format("Firmware parsing error at port {0}.", portName), ex); } try { logger.Info( "Response for device identification. ServerId {0}, Public Key {1}, Firmware Version {2}.", BitConverter.ToString(di.ServerId.Data), BitConverter.ToString(di.PublicKey.Data), di.FirmwareVersion.Data); } catch (Exception ex) { logger.WarnException("Logging error at port " + portName, ex); } DeviceIdentificationReadAt = DateTime.Now; } break; } } } catch (Exception ex) { logger.ErrorException(string.Format("Error at processing periodic response for {0}.", portName), ex); } }
/// <summary> /// Gets the meter response. /// </summary> /// <param name="requestFile">The request File.</param> /// <param name="responseFile">The response File.</param> /// <returns> /// The response value for a request message. /// </returns> private object GetMeterResponse(SmlFile requestFile, SmlFile responseFile) { if (responseFile == null) { Trace.WriteLine(string.Format("{0} no response file from meter.", portName)); logger.Error("No response file received."); throw new FaultException("No response sml file."); } if (requestFile == null) { Trace.WriteLine(string.Format("{0} !!! no request file available !!!", portName)); logger.Error("No request file available."); throw new FaultException("No request sml file."); } foreach (var msg in requestFile) { if (msg.OpenRequest != null || msg.CloseRequest != null) { continue; } // find request transactionId var reqTransacationId = msg.TransactionId; // search result message with matching transactionId var resMsg = responseFile.FirstOrDefault(m => m.TransactionId.SequenceEqual(reqTransacationId)); if (resMsg == null) { Trace.WriteLine(string.Format("{0} no response message for request found.", portName)); logger.Error("No response for request found."); throw new FaultException("No response message for request found."); } // check result message for attentionResponse if (resMsg.AttentionResponse != null) { Trace.WriteLine(string.Format("{0} Attention response for request found.", portName)); // AttentionCode.Ok returned return resMsg.AttentionResponse; } // check for event log readout if (msg.GetProfileListRequest != null) { if (msg.GetProfileListRequest.TreePath[0] == (long)ObisId.EdlEventLog) { //var listResponses = new SmlListOfType<SmlGetProfileListRes>(); var listResponses = new SmlList(); foreach (var m in responseFile) { if (m.GetProfileListResponse == null) { continue; } if (m.GetProfileListResponse.TreePath[0] == (long)ObisId.EdlEventLog) { listResponses.Add(m.GetProfileListResponse); } } return listResponses; } } // extract value response from result message if (resMsg.GetProcParameterResponse != null) { Trace.WriteLine(string.Format("{0} response message for GetProcParam found.", portName)); var procParamRes = resMsg.GetProcParameterResponse; if (procParamRes.Tree.Childs != null && procParamRes.Tree.Childs.Count > 0) { // return tree e.g. in case of deviceId response return procParamRes.Tree; } // return single value return procParamRes.Tree.Value; } // return message body if (resMsg.GetListResponse != null) { return resMsg.GetListResponse; } logger.Error("No response message for request found."); throw new DataException("No response message for request found."); } return null; }
/// <summary> /// Sends the file. /// </summary> /// <param name="requestFile">The SML file.</param> /// <param name="isManufacturerMode">if set to <c>true</c> [is manufacturer mode].</param> /// <returns> /// True if file could be written down the serial interface. /// </returns> private async Task<object> SendFile(SmlFile requestFile, bool isManufacturerMode = false) { if (requestFile == null) { throw new ArgumentNullException("requestFile"); } if (SerialPort.GetPortNames().Contains(portName) == false) { logger.Error("Port {0} is not existing.", portName); throw new ArgumentException("Port " + portName + " is not existing"); } Core.Obis.ObisId reqObis; if (requestFile[1].GetProcParameterRequest != null) { reqObis = requestFile[1].GetProcParameterRequest.TreePath.First(); logger.Warn("READ OBIS: {0}", reqObis.ToHexString()); } else if (requestFile[1].SetProcParameterRequest != null) { reqObis = requestFile[1].SetProcParameterRequest.TreePath.First(); logger.Warn("WRITE OBIS: {0}", reqObis.ToHexString()); } else { throw new InvalidOperationException(); } var sendBuff = new List<byte>(); requestFile.Serialize(sendBuff); logger.Debug("Sending SML file of size {0}.", sendBuff.Count); using (await asyncLock.LockAsync().ConfigureAwait(false)) //using (var token = new CancellationTokenSource(1000)) { // try to read response from meter try { // WriteAsync does not work with Advantech boxes //await serialPort.Write(sendBuff.ToArray(), 0, sendBuff.Count); //.WriteAsync(sendBuff.ToArray(), 0, sendBuff.Count, token.Token).ConfigureAwait(false); logger.Trace("TX: {0}", BitConverter.ToString(sendBuff.ToArray())); logger.Debug("File sent, waiting for response from meter."); var receivedFile = await ReceiveSmlFile(TimeSpan.FromSeconds(8)) .ConfigureAwait(false); // process received file var result = GetMeterResponse(requestFile, receivedFile); return result; } catch (Exception ex) { logger.ErrorException("ReceiveSmlFile failed: " + ex.StackTrace, ex); throw; } } }
/// <summary> /// Sends the request file. /// </summary> /// <param name="requestFile">The request file.</param> /// <param name="enableManufacturerMode">if set to <c>true</c> [enable manufacturer mode].</param> private async Task SendRequestFile(SmlFile requestFile, bool enableManufacturerMode) { this.logger.Info("Sending request file"); var response = await this.SendFile(requestFile, enableManufacturerMode); var attentionRes = response as Core.Sml.Messages.AttentionResponse; if (attentionRes == null) { this.logger.Error("No response received."); throw new DataException("No response received."); } if (attentionRes.AttentionNo != (ulong)Sml.AttentionCode.Ok) { this.logger.Error("Device returned an error: {0}.", attentionRes.AttentionNo); throw new DataException($"Device returned an error: {attentionRes.AttentionNo}."); } }
/// <summary> /// Adds the open request. /// </summary> /// <param name="reqFile">The req file.</param> private void AddOpenRequest(SmlFile reqFile) { var msg = new Core.Sml.Messages.Message { TransactionId = new byte[] { (byte)(reqFile.Count + 1) }, GroupNo = 0x00, OpenRequest = new Core.Sml.Messages.OpenRequest() { ClientId = this.clientId, ReqFileId = new byte[] { this.requestIndex++ }, } }; //var msg = Sml.SmlMessageFactory.OpenReq(this.clientId, new byte[] { this.requestIndex++ }); //var transactionId = new byte[] { (byte)(reqFile.Count + 1) }; //msg.TransactionId = new SmlOctetstring(transactionId); //msg.GroupNo = 0x00; reqFile.Add(msg); if (this.requestIndex > 200) { this.requestIndex = 0; } }
/// <summary> /// Creates the set request file for tariff. /// </summary> /// <param name="portId">The port id.</param> /// <param name="bitMask">The bit mask.</param> /// <param name="tariffAplus">The tariff aplus.</param> /// <param name="tarrifAminus">The tarrif aminus.</param> /// <returns>The request file to set the tariffs and bitmask.</returns> public static SmlFile CreateSetRequestFileForTariff( string portId, UnsignedParam bitMask, UnsignedParam tariffAplus, UnsignedParam tarrifAminus) { var handler = new SmlHandler(portId); var smlFile = new SmlFile(); handler.AddOpenRequest(smlFile); handler.AddSetBooleanParamRequest( smlFile, new BooleanParam() { ObisCode = (long)ObisId.EdlOperationModeEdl40, Data = false }); handler.AddSetUnsignedParamRequest(smlFile, bitMask); if (tariffAplus != null) { handler.AddSetUnsignedParamRequest(smlFile, tariffAplus); } if (tarrifAminus != null) { handler.AddSetUnsignedParamRequest(smlFile, tarrifAminus); } handler.AddCloseRequest(smlFile); return smlFile; }
private static SmlFile ReceiveSmlFile(SerialPort sp, TimeSpan timeOut) { var sw = new Stopwatch(); var resetEvent = new AutoResetEvent(false); var rcvBuff = new List<byte>(); var buff = new byte[1024]; var escEoF = new byte[] { 0x1b, 0x1b, 0x1b, 0x1b, 0x1a }; var escBoF = new byte[] { 0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01 }; var smlFile = new SmlFile(); sw.Start(); while (sw.Elapsed < timeOut) { resetEvent.WaitOne(100); if (sp.BytesToRead < 1 && rcvBuff.Count <= 30) { resetEvent.WaitOne(10); continue; } if (sp.BytesToRead > 0) { int bytesRead = sp.Read(buff, 0, buff.Count()); for (int i = 0; i < bytesRead; i++) { rcvBuff.Add(buff[i]); } //this.logger.Trace("RX: {0}", BitConverter.ToString(buff, 0, bytesRead)); } // check begin of file if (rcvBuff.Count < 8) { continue; } var fileHeader = rcvBuff.GetRange(0, 8); if (fileHeader.SequenceEqual(escBoF) == false) { rcvBuff.RemoveAt(0); continue; } // check end of file var fileTrailer = rcvBuff.GetRange(rcvBuff.Count - 8, 5); if (fileTrailer.SequenceEqual(escEoF) == false) { continue; } try { smlFile.Parse(rcvBuff); //this.logger.Debug("SML file of size {0} received.", rcvBuff.Count); return smlFile; } catch (SmlParseFailedException ex) { //this.logger.ErrorException("Parsing incoming file failed.", ex); Trace.WriteLine(string.Format("Port {0}. Parsing incoming file failed: {0}.", portName), ex.Message); // return file if there's a message inside. //if (smlFile.Count > 0) //{ // this.FileReceived = smlFile; // return smlFile; //} continue; } } return smlFile; }