laatzen/Common/Service/MeterProcessState/Controllers/OrderController.cs
Roland Drabesch ff5a1a8cd9 merge
2025-12-02 10:24:55 +01:00

670 lines
29 KiB
C#

using LaaPackages.SqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using Xylem.Common.Logic.ServiceCore;
namespace Xylem.Common.Service.MeterProcessState.Controllers
{
[RoutePrefix("api/Order")]
public class OrderController : ApiController
{
public static class GlobalConfig
{
public static Lazy<String> connectionString = new Lazy<String>(()
=>
System.Configuration.ConfigurationManager.ConnectionStrings["default"].ConnectionString
);
}
[Route("GetCordonelOrders"), HttpGet]
public async Task<HttpResponseMessage> GetCordonelOrders(String Type = "GNS")
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" SELECT TOP (1000) [AuftragNr] ");
sb.AppendLine($" ,apG.[PositionNr]");
sb.AppendLine($" ,apG.[Menge]");
sb.AppendLine($" ,apG.[SollDatum]");
sb.AppendLine($" ,apG.FertigungsauftragNr");
sb.AppendLine($" ,apG.SerienNrVon");
sb.AppendLine($" ,apG.SerienNrBis");
sb.AppendLine($" FROM [Auftrag].[dbo].[AuftragPosition_Gesamt] apG ");
sb.AppendLine($" inner join [Auftrag].[dbo].[Identnr] ident " +
$" on apG.[IdentNr] = ident.[IdentNr] ");
sb.AppendLine($" where ident.TYP like '%{Type}%' ");
sb.AppendLine($" order by AuftragNr desc, PositionNr desc ");
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("GetCordonelMapping"), HttpGet]
public async Task<HttpResponseMessage> GetCordonelMapping(Int32 PcbId)
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" SELECT distinct pcb.PcbId, PickingOrder.CordonelAssignedPicking_FertigungsAuftragsNr, PressureOrder.CordonelPressureTest_FertigungsAuftragsNr, map.MapPcbIdToSerialNumber_SerialNumber ");
sb.AppendLine($" FROM Genesis_PcbPassword pcb ");
sb.AppendLine($" left outer join Auftrag.dbo.Cordonel_AssignedPicking PickingOrder on pcb.PcbId = PickingOrder.CordonelAssignedPicking_PcbId and PickingOrder.CordonelAssignedPicking_IsDeleted = 0 ");
sb.AppendLine($" left outer join Auftrag.dbo.Cordonel_PressureTest PressureOrder on pcb.PcbId = PressureOrder.CordonelPressureTest_PcbId and PressureOrder.CordonelPressureTest_IsDeleted = 0 ");
sb.AppendLine($" left outer join [Auftrag].[dbo].[MapPcbIdToSerialNumber] map on pcb.PcbId = map.MapPcbIdToSerialNumber_PcbId and map.MapPcbIdToSerialNumber_Deleted = 0 ");
sb.AppendLine($" where pcb.PcbId = CAST( {PcbId} as nvarchar(25)) ");
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("DeleteCordonelMapping"), HttpPost]
public async Task<HttpResponseMessage> DeleteCordonelMapping(Int32 PcbId)
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" delete Auftrag.dbo.Cordonel_AssignedPicking where CordonelAssignedPicking_PcbId = {PcbId} ; ");
sb.AppendLine($" delete Auftrag.dbo.Cordonel_PressureTest where CordonelPressureTest_PcbId = {PcbId} ; ");
sb.AppendLine($" update AuftragPositionSerienNr set StatusFertigung = 25 where SerienNr in (select MapPcbIdToSerialNumber_SerialNumber from Auftrag.[dbo].[MapPcbIdToSerialNumber] where MapPcbIdToSerialNumber_PcbId = {PcbId}); ");
sb.AppendLine($" update [Auftrag].[dbo].[CordonelMeterCalibrationResults] set PcbID = PcbID * -1 where PcbID = {PcbId} ");
sb.AppendLine($" delete Auftrag.[dbo].[MapPcbIdToSerialNumber] where MapPcbIdToSerialNumber_PcbId = {PcbId} ; ");
sb.AppendLine($" update Genesis_PcbPassword set PcbId = cast(PcbId as int) *-1 where pw.PcbId = '{PcbId}' ; ");
sb.AppendLine($" select 1 ");
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("GetCordonelOrderDetails"), HttpGet]
public async Task<HttpResponseMessage> GetCordonelOrderDetails(Int32 OrderNr)
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" declare @FertigungsauftragNr int = {OrderNr} ");
sb.AppendLine($" select * from ");
sb.AppendLine($" (select apG.[AuftragNr],apG.[PositionNr], apG.FertigungsauftragNr, SerienNr, Frequenz, Adresse FROM [Auftrag].[dbo].[AuftragPosition_Gesamt] apG ");
sb.AppendLine($" inner join Auftrag.dbo.AuftragPositionSerienNr srn on srn.Pruefgangnr = 0 and apG.AuftragNr = srn.AuftragNr and apG.PositionNr = srn.PositionNr");
sb.AppendLine($" inner join Genesis_Meter m on srn.SerienNr = m.Seriennummer");
sb.AppendLine($" where apG.FertigungsauftragNr = @FertigungsauftragNr and srn.Pruefgangnr = 0 ) a ");
sb.AppendLine($" left outer join (select CordonelAssignedPicking_PcbId,CordonelAssignedPicking_Date, MapPcbIdToSerialNumber_SerialNumber,MapPcbIdToSerialNumber_DateUtc ");
sb.AppendLine($" from Auftrag.dbo.Cordonel_AssignedPicking ap ");
sb.AppendLine($" left outer join Auftrag.dbo.Cordonel_PressureTest pt on ap.CordonelAssignedPicking_PcbId = pt.CordonelPressureTest_PcbId ");
sb.AppendLine($" left outer join Auftrag.dbo.MapPcbIdToSerialNumber map on ap.CordonelAssignedPicking_PcbId = map.MapPcbIdToSerialNumber_PcbId ");
sb.AppendLine($" where ap.CordonelAssignedPicking_FertigungsAuftragsNr = @FertigungsauftragNr ");
sb.AppendLine($" and CordonelAssignedPicking_IsDeleted = 0 ");
sb.AppendLine($" and (CordonelPressureTest_IsDeleted = 0 or CordonelPressureTest_IsDeleted is null) ");
sb.AppendLine($" and (MapPcbIdToSerialNumber_Deleted = 0 or MapPcbIdToSerialNumber_Deleted is null) ");
sb.AppendLine($" ) b on a.SerienNr = b.MapPcbIdToSerialNumber_SerialNumber ");
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("GetCordonelCustomers"), HttpGet]
public async Task<HttpResponseMessage> GetCordonelCustomers(String FilterName = null)
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" select distinct kunden.KundenNr as CustomerNumber, kunden.Name as CustomerName, kunden.Ort as CustomerLocation ");
sb.AppendLine($" from Identnr ident ");
// sb.AppendLine($" inner join Statistik_auftrag auftrag on auftrag.IdentNr = ident.IdentNr ");
sb.AppendLine($" inner join AuftragPosition_Gesamt auftragPOS on auftragPOS.IdentNr = ident.IdentNr ");
sb.AppendLine($" inner join Auftrag_Gesamt auftrag on auftrag.AuftragNr = auftragPOS.AuftragNr ");
sb.AppendLine($" inner join Kunde kunden on auftrag.KundenNr = kunden.KundenNr ");
sb.AppendLine($" where ident.Typ = 'GNS' ");
sb.AppendLine($" and kunden.KundenNr <> 99999 ");
if (!string.IsNullOrEmpty(FilterName))
{
sb.AppendLine($" and kunden.Name like '{FilterName}' ");
}
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("GetCustomersCordonelOrders"), HttpGet]
public async Task<HttpResponseMessage> GetCustomersCordonelOrders(Int32 CustomerNumber)
{
try
{
var customersOrders = SqlConnection
.CreateSqlConnection(GlobalConfig.connectionString.Value)
.CreateCommand($@"
SELECT apg.AuftragNr
, apg.PositionNr
, apg.Menge
, idn.Katalognummer
, idn.Nennweite
, idn.Baulaenge
, vm.Charcode
, cms.Dn_Name
, cms.Dn_InternalId
FROM Identnr AS idn
INNER JOIN AuftragPosition_Gesamt AS apg
ON idn.IdentNr = apg.IdentNr
INNER JOIN VAKO_Merkmale AS vm
on vm.Basis = 'GNS'
and vm.Name = 'C_GEN_NENNWEITE'
and vm.Charcode = SUBSTRING(idn.VakoCode, vm.Stelle, vm.Laenge)
INNER JOIN CordonelMeterSizes AS cms
ON vm.Charcode = cms.Dn_VakoID
INNER JOIN Auftrag_Gesamt AS ag
ON apg.AuftragNr = ag.AuftragNr
where idn.Typ = 'GNS'
and ag.KundenNr = @{nameof(CustomerNumber)}")
.SetParameter(nameof(CustomerNumber), CustomerNumber)
.ExecuteReader(x => new
{
CustomerOrderNumber = x.GetValue<Int32>(0),
CustomerOrderPos = x.GetValue<Int16>(1),
Quantity = x.GetValue<Int32>(2),
CatalogNumber = x.GetValue<String>(3),
Diameter = x.GetValue<Int32>(4),
Length = x.GetValue<Int32>(5),
CordonelMeterSizeCode = x.GetValue<String>(6),
CordonelMeterSizeName = x.GetValue<String>(7),
CordonelMeterSizeId = x.GetValue<Int32>(8),
});
return this.Request.CreateResponse(HttpStatusCode.OK, customersOrders);
}
catch (Exception ex)
{
return this.Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
[Route("GetCordonelOrderList"), HttpGet]
public async Task<HttpResponseMessage> GetCordonelOrderList(Int32 CustomerOrderNumber, Int32 CustomerOrderPos)
{
try
{
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" select distinct KundeneigeneSerienNr as CustomerSerialNumber,SerienNr as SerialNumber ,map.MapPcbIdToSerialNumber_PcbId as PcbId ");
sb.AppendLine($" from AuftragPositionSerienNr ");
sb.AppendLine($" inner join MapPcbIdToSerialNumber map on SerienNr = map.MapPcbIdToSerialNumber_SerialNumber ");
sb.AppendLine($" where AuftragNr = {CustomerOrderNumber} and PositionNr = {CustomerOrderPos} ");
var dt = dataAccess.ExecuteQuery(sb.ToString());
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return dt; }));
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex);
}
}
public class MeterPasswordContainer
{
public Int64? orderNumber = null;
public Int64? radioAdress = null;
public String Skeleton = "";
public List<Byte[]> listOfHashes = new List<Byte[]>();
public List<Byte[]> listOfPasswords = new List<Byte[]>();
public String KeySetId { get; internal set; }
}
private MeterPw getKeys(String Token, String orderNumber, String radioAdress)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
return true;
};
//todo: amis haben keine radio adresse (pcbid)
var Url = $"https://nodes.sms-esaap.com:8081/api/v3/custom/keysetkeys?orderNumber={orderNumber}&radioAdress={radioAdress}";
var http = (HttpWebRequest)WebRequest.Create(Url);
http.Headers.Add("Cache-Control", "no-cache");
//todo: X-noed-Token
http.Headers.Add("X-Node-Token", "d5b8c0b2-81f6-4421-80d0-44eb2093e616");
try
{
var response = (HttpWebResponse)http.GetResponse();
var responseStream = response.GetResponseStream();
if (responseStream == null)
{
throw new InvalidOperationException();
}
if (response.StatusCode != HttpStatusCode.OK)
{
throw new ApplicationException("Unable to get keys from server");
}
using (var sr = new StreamReader(responseStream))
{
var responseJson = sr.ReadToEnd();
responseJson = responseJson.Trim('"');
return MeterPw.FromJson(responseJson);
}
}
catch (Exception ex)
{
throw new ApplicationException("Fatal exception while getting keys from server", ex);
}
}
[Route("GetPasswordHashFileContent")]
public async Task<HttpResponseMessage> GetPasswordHashFileContent(String PcbId)
{
try
{
var listOfHashes = new List<Byte[]>();
var sbOutPut = new StringBuilder();
var sbQuery = new StringBuilder();
sbQuery.AppendLine($" SELECT ");
sbQuery.AppendLine($" [Seriennummer] ");
sbQuery.AppendLine($" ,mps.MapPcbIdToSerialNumber_PcbId ");
sbQuery.AppendLine($" ,[Adresse] ");
sbQuery.AppendLine($" ,AuftragPosSNr.AuftragNr ");
sbQuery.AppendLine($" ,Auftrag.FertigungsauftragNr ");
sbQuery.AppendLine($" ,[GeschlossenDatum] ");
sbQuery.AppendLine($" ,[Vergabe_am] ");
sbQuery.AppendLine($" ,passwordtbl.KeySetId ");
sbQuery.AppendLine($" ,passwordtbl.orderNumber ");
sbQuery.AppendLine($" ,passwordtbl.Password as Skeleton ");
sbQuery.AppendLine($" ,passwordtbl.ProcessorID as ProcessorUid ");
sbQuery.AppendLine($" FROM [Auftrag].[dbo].[Genesis_Meter] meter ");
sbQuery.AppendLine($" inner join MapPcbIdToSerialNumber mps" +
$" on mps.MapPcbIdToSerialNumber_SerialNumber = meter.Seriennummer and mps.MapPcbIdToSerialNumber_Deleted = 0 ");
sbQuery.AppendLine($" inner join[Auftrag].[dbo].[AuftragPositionSerienNr] AuftragPosSNr " +
$" on AuftragPosSNr.SerienNr = meter.Seriennummer and [Pruefgangnr] = 0 ");
sbQuery.AppendLine($" inner join[Auftrag].[dbo].[AuftragPosition] Auftrag" +
$" on Auftrag.AuftragNr = AuftragPosSNr.AuftragNr and AuftragPosSNr.PositionNr = Auftrag.PositionNr ");
sbQuery.AppendLine($" left outer join[dbo].[Genesis_PcbPassword] passwordtbl" +
$" on passwordtbl.PcbId = mps.MapPcbIdToSerialNumber_PcbId ");
sbQuery.AppendLine($" where mps.MapPcbIdToSerialNumber_PcbId = '{PcbId}' ");
var result = new MeterPasswordContainer();
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
var dt = dataAccess.ExecuteQuery(sbQuery.ToString());
foreach (var item in dt.Select())
{
result.orderNumber = (Int32)item["FertigungsauftragNr"];
result.radioAdress = (Int64)item["Adresse"];
result.Skeleton = (String)item["Skeleton"];
if (item["KeySetId"].GetType() != typeof(System.DBNull))
{
result.KeySetId = (String)item["KeySetId"];
}
}
}
if (!result.orderNumber.HasValue || !result.radioAdress.HasValue)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, "PcbId not found");
}
if (string.IsNullOrEmpty(result.KeySetId))
{
result.KeySetId = CreateKeySet(7);
//mapp keyset
MapKeySet(result.KeySetId, result.orderNumber.Value, result.radioAdress.Value);
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
var dt = dataAccess.ExecuteQuery(
$"update auftrag.[dbo].[Genesis_PcbPassword] set KeySetId = '{result.KeySetId}' where PcbId = '{PcbId}'; select * from auftrag.[dbo].[Genesis_PcbPassword] where PcbId = '{PcbId}'");
}
}
var passwords = getKeys("", result.orderNumber.Value.ToString(), result.radioAdress.Value.ToString());
passwords.Keys.Insert(2, new Key() { Index = 2, KeyKey = result.Skeleton });
foreach (var keyLVL in passwords.Keys)
{
using (System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed())
{
var rawKey = keyLVL.KeyKey;
if (rawKey.Length > 12)
{
rawKey = rawKey.Substring(0, 12);
}
if (result.listOfPasswords.Count <= 7)
{
result.listOfPasswords.Add(Encoding.UTF8.GetBytes(rawKey));
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(rawKey));
result.listOfHashes.Add(hash);
}
}
}
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() =>
{
return result;
}));
//if (resultBinary)
//{
// return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() =>
// {
// return listOfHashes;
// }));
//}
//else
//{
// return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() =>
// {
// var sbResult = new StringBuilder();
// foreach (var hashResult in listOfHashes)
// {
// foreach (var b in hashResult)
// {
// sbResult.Append(b.ToString("X2"));
// }
// sbResult.AppendLine();
// }
// return sbResult.ToString();
// }));
//}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex.ToString() + "<br >" + ex.InnerException.ToString());
}
}
private String MapKeySet(String KeySetID, Int64 orderNumber, Int64 radioAdress)
{
var Url = $"https://nodes.sms-esaap.com:8081/api/v3/custom/keysets/devices";
var http = (HttpWebRequest)WebRequest.Create(Url);
http.Headers.Add("Cache-Control", "no-cache");
http.Headers.Add("X-Node-Token", "d5b8c0b2-81f6-4421-80d0-44eb2093e616");
String postData = $"[{{\"setId\":\"{KeySetID}\", \"orderNumber\":\"{orderNumber}\", \"radioAdress\":\"{radioAdress}\"}}]";
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] byte1 = encoding.GetBytes(postData);
// Set the content type of the data being posted.
http.ContentType = "application/json; charset=utf-8";
http.Method = "POST";
// Set the content length of the string being posted.
http.ContentLength = byte1.Length;
Stream newStream = http.GetRequestStream();
newStream.Write(byte1, 0, byte1.Length);
newStream.Close();
var response = (HttpWebResponse)http.GetResponse();
var responseStream = response.GetResponseStream();
if (responseStream == null)
{
throw new InvalidOperationException();
}
if (response.StatusCode != HttpStatusCode.OK)
{
}
using (var sr = new StreamReader(responseStream))
{
var responseJson = sr.ReadToEnd();
responseJson = responseJson.Trim('"');
return responseJson.ToString();
}
}
private String CreateKeySet(Int32 CountKeys)
{
var Url = $"https://nodes.sms-esaap.com:8081/api/v3/custom/keysets";
var http = (HttpWebRequest)WebRequest.Create(Url);
http.Headers.Add("Cache-Control", "no-cache");
http.Headers.Add("X-Node-Token", "d5b8c0b2-81f6-4421-80d0-44eb2093e616");
String postData = $"{{\"setCount\": 1,\"keysPerSet\": {CountKeys} }}";
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] byte1 = encoding.GetBytes(postData);
// Set the content type of the data being posted.
http.ContentType = "application/json; charset=utf-8";
http.Method = "POST";
// Set the content length of the string being posted.
http.ContentLength = byte1.Length;
Stream newStream = http.GetRequestStream();
newStream.Write(byte1, 0, byte1.Length);
newStream.Close();
var response = (HttpWebResponse)http.GetResponse();
var responseStream = response.GetResponseStream();
if (responseStream == null)
{
throw new InvalidOperationException();
}
if (response.StatusCode != HttpStatusCode.OK)
{
}
using (var sr = new StreamReader(responseStream))
{
var responseJson = sr.ReadToEnd();
responseJson = responseJson.Trim('"');
var res = MeterPw.ArrayFromJson(responseJson);
return res[0].SetId.ToString();
}
}
public partial class MeterPw
{
[JsonProperty("SetId")]
public Guid SetId { get; set; }
[JsonProperty("Keys")]
public List<Key> Keys { get; set; }
}
public partial class Key
{
[JsonProperty("Index")]
public Int64 Index { get; set; }
[JsonProperty("Key")]
public String KeyKey { get; set; }
}
public partial class MeterPw
{
public static MeterPw Empty() => new MeterPw() { SetId = Guid.Empty, Keys = new List<Key>() };
public static MeterPw FromJson(String json) => JsonConvert.DeserializeObject<MeterPw>(json, Converter.Settings);
public static MeterPw[] ArrayFromJson(String json) => JsonConvert.DeserializeObject<MeterPw[]>(json, Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
[Route("GetKeySetId")]
public async Task<HttpResponseMessage> GetKeySetId(String PcbId)
{
try
{
var returnKeySetId = "Not assigned";
using (var dataAccess = new SqlDataAccess(GlobalConfig.connectionString.Value))
{
var dt = dataAccess.ExecuteQuery($"select [KeySetId] from dbo.Genesis_PcbPassword where PcbId like '{PcbId}'");
foreach (var item in dt.Select())
{
returnKeySetId = item["KeySetId"].ToString();
}
}
return Request.CreateResponse(HttpStatusCode.OK, await Task.Run(() => { return returnKeySetId; }));
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, ex.ToString() + "<br >" + ex.InnerException.ToString());
}
}
}
}