Last active
September 29, 2016 14:08
-
-
Save ChrisMoney/1116b66223cf50df606d2ca3b3f1a637 to your computer and use it in GitHub Desktop.
TCP (Transmission Control Protocol) Methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.IO; | |
using System.Net.Sockets; | |
using System.Runtime.Serialization; | |
using System.Text; | |
using System.Xml; | |
using System.Xml.Serialization; | |
using ClassLibrary_Accounts; | |
using System.Text.RegularExpressions; | |
using System.Drawing; | |
using System.Drawing.Imaging; | |
using IntercardLogging; | |
namespace SocketSender | |
{ | |
public class TransActionServerIO : TcpTransaction | |
{ | |
#region Transaction IO | |
public IntercardLogger ILog = new IntercardLogger(); | |
public string ErrorMessage { get; private set; } | |
public int ErrorCode { get; private set; } | |
protected Event NewEvent(TransactionType type) | |
{ | |
int deviceTag = -1; | |
int empId = -1; | |
string[] macIds = GetLocalMACs().ToArray(); | |
int locId = 0; | |
Event ev = new Event(deviceTag, empId, empId, macIds, locId, -1); | |
ev.EventType = type; | |
return ev; | |
} | |
public event EventHandler<TransactionServerCommErr> ErrorEvent; | |
protected void OnErrorEvent(int errorCode, string errorMessage) | |
{ | |
ErrorCode = errorCode; | |
ErrorMessage = errorMessage; | |
if (ErrorEvent != null) | |
ErrorEvent(this, new TransactionServerCommErr(errorCode, errorMessage)); | |
} | |
public TransActionServerIO() : base(eofMarker: "<EOF>") | |
{ | |
Port = Properties.Settings.Default.PrimaryPORT; | |
ErrorCode = 0; | |
ErrorMessage = "No Error"; | |
} | |
protected override T Send<T>(object input) { return Send<T>(input, firstTry: true); } | |
protected T Send<T>(object input, bool firstTry) | |
{ | |
try | |
{ | |
CurrentIP.RefreshTsHealth(); | |
Ip = CurrentIP.TransactionServerIP; | |
return base.Send<T>(input); | |
} | |
catch (Exception ex) | |
{ | |
ExceptionHandleAction action = HandleException(ex, input, firstTry); | |
switch (action) | |
{ | |
case ExceptionHandleAction.FailReturnNull: | |
return default(T); | |
case ExceptionHandleAction.Retry: | |
return Send<T>(input, firstTry: false); | |
case ExceptionHandleAction.ThrowInner: | |
string msg = ex.InnerException.GetType().Name + ": " + ex.InnerException.Message; | |
throw new Exception(msg); | |
case ExceptionHandleAction.Throw: | |
default: | |
break; | |
} | |
throw; | |
} | |
} | |
private enum ExceptionHandleAction | |
{ | |
Retry, | |
FailReturnNull, | |
Throw, | |
ThrowInner | |
} | |
private ExceptionHandleAction HandleException(Exception ex, object input, bool firstTry) | |
{ | |
Exception ex2 = ex.InnerException; | |
SocketException socketEx = (ex as SocketException) ?? (ex2 as SocketException); | |
TransactionServerResponseErrorException responseError = ex as TransactionServerResponseErrorException; | |
Exception serializationException = null; | |
if (ex is InvalidOperationException && ex2 != null) | |
serializationException = ex2; | |
if (responseError != null) | |
return ExceptionHandleAction.Throw; | |
if (socketEx != null) | |
{ | |
//notify bad server | |
CurrentIP.SetUnhealthy(Ip); | |
if (firstTry && !string.IsNullOrWhiteSpace(CurrentIP.TransactionServerIP)) | |
{ | |
//if a backup is available, try it | |
return ExceptionHandleAction.Retry; | |
} | |
else | |
{ | |
//if there is only one unique TS, or this is the second try, generate error | |
string msg = string.Format("send() Socket PORT:{0}, IP:{1}\n{2}", Port, Ip, socketEx.Message); | |
ILog.IntercardLog().Debug(msg); | |
return ExceptionHandleAction.FailReturnNull; | |
} | |
} | |
if(serializationException != null) | |
return ExceptionHandleAction.ThrowInner; | |
return ExceptionHandleAction.Throw; | |
} | |
public class TransactionServerResponseErrorException : Exception | |
{ | |
public TransactionServerResponseErrorException(string message) : base(message) { } | |
public TransactionServerResponseErrorException(string message, Exception innerException) : base(message, innerException) { } | |
} | |
protected override T ReadResponse<T>(Stream stream) | |
{ | |
XmlTextReader reader = null; | |
//Note: XmlTextReader likes to dispose the stream, so make sure you don't need it anymore. (Logs still work though) | |
// Also, I'm not 100% sure XmlTextReader needs to be disposed at all if the base stream is handled elsewhere. | |
// Probably need to look into it. | |
try | |
{ | |
reader = new XmlTextReader(stream); | |
reader.MoveToContent(); | |
XmlReader sub = reader.ReadSubtree(); //Read just the root element, not the subsequent <EOF> | |
XmlSerializer serializer = SerializerHelper.Get(typeof(T)); | |
return (T) serializer.Deserialize(sub); | |
} | |
catch (XmlException xmlEx) | |
{ | |
//this catches malformed xml. The primary suspect is the TS's unrecognized event type errors, which look like: | |
//{<?xml version="1.0" encoding="utf-8"?>Exception occurred while processing request<EOF>} | |
//Drives me nuts that it has an XML declaration and casually ignores it, but at least it's consistent. | |
//Since we don't have a validated structure we can't make assumptions about where the end is. | |
//To be safe, we are going to try and stop reading at the <EOF> if it is present. Otherwise, we'll have to wait | |
//for the remote socket to close or our TCPClient to timeout. | |
//Note: the EOF state machine is now integrated into this ReplayStream, so we're good on that. | |
stream.Position = 0; | |
string contents; | |
try { contents = new StreamReader(stream).ReadToEnd(); } | |
catch { contents = this.TxtReceived; } | |
if (contents == null) | |
throw; | |
Match m = Regex.Match(contents, @"^(?:\s*<\?.*\?>\s*)*(.+?)\s*(?:<EOF>)?$", RegexOptions.Multiline | RegexOptions.CultureInvariant); | |
if (!m.Success || m.Groups.Count < 2 || m.Groups[1].Length == 0) | |
throw; | |
throw new TransactionServerResponseErrorException(string.Format("TS Error Response: \"{0}\"", m.Groups[1].Captures[0].Value), xmlEx); | |
} | |
catch (InvalidOperationException ex) //Catches if the serializer complains about something. | |
{ | |
Exception trueEx = ex.InnerException ?? ex; | |
throw new TransactionServerResponseErrorException("De-serialization error: " + trueEx.Message, trueEx); | |
} | |
finally | |
{ | |
if(reader != null) | |
((IDisposable)reader).Dispose(); | |
} | |
} | |
protected override void WriteRequest(object data, Stream stream) | |
{ | |
var serializer = SerializerHelper.Get(data.GetType()); | |
var ns = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }); //strip all default namespaces. | |
var settings = new XmlWriterSettings { Encoding = new UTF8Encoding(false), OmitXmlDeclaration = false }; | |
using (var xw = XmlTextWriter.Create(stream, settings)) | |
{ | |
if (settings.OmitXmlDeclaration) | |
{ | |
//'encoding' in the xml declaration used to be forced to all-caps. As far as I can tell, there's no reason for it. | |
xw.WriteRaw("<?xml version=\"1.0\" encoding=\"UTF-8\">"); | |
} | |
serializer.Serialize(xw, data, ns); | |
xw.WriteRaw("<EOF>"); | |
} | |
} | |
// resize profile photo | |
protected static byte[] ResizeBitmap(byte[] byteArrayIn, int newWidth, int newHeight) | |
{ | |
using (var msIn = new MemoryStream(byteArrayIn)) | |
{ | |
var oldImage = Bitmap.FromStream(msIn); | |
var newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format16bppRgb565); | |
Graphics.FromImage(newImage).DrawImage(oldImage, 0, 0, newWidth, newHeight); | |
using (var msOut = new MemoryStream()) | |
{ | |
newImage.Save(msOut, ImageFormat.Bmp); | |
msOut.Position = 0; | |
return msOut.ToArray(); | |
} | |
} | |
} | |
#endregion | |
#region Transaction Server Messages | |
public Account GetCardHoldersBalance(long accountNumber) | |
{ | |
try | |
{ | |
Event ev = NewEvent(TransactionType.Balance); | |
ev.AccountBalance = new Account { AccountNumber = accountNumber }; | |
// send data to transaction server | |
var response = Send<Account>(ev); | |
if (response == null) | |
{ | |
string msg = string.Format("No Response, null returned. PORT:{0}, IP:{1}", Port, (Ip ?? "(null)")); | |
ILog.IntercardLog().Debug(msg); | |
OnErrorEvent(1, "No balance data"); | |
return null; | |
} | |
return response; | |
} | |
catch (Exception ex) | |
{ | |
//This error message is different. Left as it was originally. | |
string s = string.Format("Account - {0}", accountNumber); | |
ILog.IntercardLog().Debug(s + "\n" + ex.Message + "\n" + ex.TargetSite + "\n" + ex.StackTrace); | |
OnErrorEvent(1, ex.Message); | |
System.Console.WriteLine(ex); | |
return null; | |
} | |
} | |
public bool UpdateCardHoldersBalance(CreditDebitAccounts accountTrans) | |
{ | |
if (Properties.Settings.Default.TrainingMode == true) | |
return true; | |
try | |
{ | |
foreach (CreditDebitAccount card in accountTrans.CreditDebitAccountArray) | |
if (card.Photo != null && card.Photo.Length > 0) | |
card.Photo = ResizeBitmap(card.Photo, 130, 120); //resize to 130x120 | |
Event ev = NewEvent(TransactionType.CreditDebitAccounts); | |
ev.CreditOrDebitAccount = accountTrans; | |
ev.CreditOrDebitAccount.DeviceTag = ev.Device; | |
ev.CreditOrDebitAccount.GMT_TimeStamp = ev.GMT_DateTime; | |
// send data to transaction server | |
var response = Send<CreditDebitAccountsResponse>(ev); | |
if (response == null) //doesn't check for response.Status != 0. Not sure if intentional | |
{ | |
OnErrorEvent(1, "no balance data"); | |
return false; | |
} | |
return true; | |
} | |
catch (Exception ex) | |
{ | |
ILog.IntercardLog().Debug(ex.Message); | |
OnErrorEvent(1, ex.Message); | |
System.Console.WriteLine(ex); | |
return false; | |
} | |
} | |
public int GetMemberByAccountNumber(string[] macIds, long cardNumber, out string firstName, out string lastName, | |
out string phone, out int sex, out string email, out string postalCode, out DateTime dateOfBirth, out byte[] photo, out bool digitalTickets) | |
{ | |
WebReference.WEB_Membership ws = new WebReference.WEB_Membership(); | |
return ws.iCashierCustomerReg_GetByAccountNumber_AA(macIds, cardNumber, out firstName, out lastName, | |
out phone, out sex, out email, out postalCode, out dateOfBirth, out photo, out digitalTickets); | |
} | |
public bool UpdateCustomerInfo(CustomerInfo customerData) | |
{ | |
if (Properties.Settings.Default.TrainingMode) | |
return true; | |
try | |
{ | |
if (customerData.CustomerPic != null && customerData.CustomerPic.Length > 0) | |
customerData.CustomerPic = ResizeBitmap(customerData.CustomerPic, 130, 120); | |
Event ev = NewEvent(TransactionType.UpdateCustomerInfo); | |
ev.CustomerData = customerData; | |
// send data to transaction server | |
var response = Send<CustomerInfoResponse>(ev); | |
if (response == null || response.Status != 0) | |
{ | |
ILog.IntercardLog().Debug(response.ErrorMessage, "Transaction Server Error"); | |
OnErrorEvent(1, response.ErrorMessage); | |
return false; | |
} | |
return true; | |
} | |
catch (Exception ex) | |
{ | |
ILog.IntercardLog().Debug(ex.Message); | |
OnErrorEvent(1, ex.Message); | |
System.Console.WriteLine(ex); | |
return false; | |
} | |
} | |
public static IEnumerable<string> GetLocalMACs() | |
{ | |
var macs = new HashSet<string>(); | |
foreach (var mo in new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration").Get()) | |
if (mo != null && mo["MacAddress"] != null) | |
macs.Add(mo["MacAddress"].ToString().Replace(":", "").Replace("-", "")); | |
return macs; | |
} | |
#endregion | |
} | |
public class TransactionServerCommErr : System.EventArgs | |
{ | |
public int Error { get; private set; } | |
public string ErrorDes { get; private set; } | |
public TransactionServerCommErr(int error, string errorDes) | |
{ | |
Error = error; | |
ErrorDes = errorDes; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment