Skip to content

Instantly share code, notes, and snippets.

@ChrisMoney
Last active September 29, 2016 14:08
Show Gist options
  • Save ChrisMoney/1116b66223cf50df606d2ca3b3f1a637 to your computer and use it in GitHub Desktop.
Save ChrisMoney/1116b66223cf50df606d2ca3b3f1a637 to your computer and use it in GitHub Desktop.
TCP (Transmission Control Protocol) Methods
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