Skip to content

Instantly share code, notes, and snippets.

@nimms
Created September 26, 2010 03:08
Show Gist options
  • Save nimms/597553 to your computer and use it in GitHub Desktop.
Save nimms/597553 to your computer and use it in GitHub Desktop.
using System;
using DDSGpsNav.ServiceLayer.Invoker;
namespace DDSGpsNav.ServiceLayer.Invoker.MFData
{
/// <summary>
/// Class which gets the latest Truck Master file from the server
/// </summary>
public class MasterFileUpdateCommand : ICommand
{
private readonly string startRecord;
private readonly MasterFileType mfType;
private readonly OperationType opType;
public MasterFileUpdateCommand(MasterFileType mfType, OperationType opType, string startRecord)
{
this.startRecord = startRecord;
this.mfType = mfType;
this.opType = opType;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"></see> class.
/// </summary>
public string execute()
{
var invoker = new InvokerFactory().BuildHttpInvoker();
var CommandBody = String.Format("[" + opType + "][" + mfType + "][{0}]", startRecord);
return invoker.DoPut(CommandBody, 1);
}
}
public enum OperationType
{
SYNC, TABLE
}
public enum MasterFileType
{
TRUCK, RUN, WAYPOINTS, EMP, BINS, PREMISES, COLL_ISSUE, PREMISES_NORFID, AREA_NOTES, MAP_REGIONS, COORDINATES,
MISSED_SERVICE
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using DDSGpsNav.Classes;
using DDSGpsNav.ServiceLayer.NetworkInfo;
using DDSGpsNav.Services.Network.Invoker;
namespace DDSGpsNav.ServiceLayer.Invoker
{
/// <summary>
/// A HttpInvoker is used to send http requests to a given url. The Url is immutable and is provided
/// via the constructor so that each HttpInvoker object is threadsafe and can be safely used
/// in concurrent environments
/// </summary>
public class HttpInvoker : IHttpInvoker
{
private readonly string serverUrl;
private readonly NetworkInfo.NetworkConnectionInfo networkConnectionInfo;
private Dictionary<string, string> Parameters;
//private DateTime RequestStartedAt;
public HttpInvoker(string serverUrl)
{
this.serverUrl = serverUrl;
}
public HttpInvoker(string serverUrl, NetworkInfo.NetworkConnectionInfo networkConnectionInfo)
{
this.serverUrl = serverUrl;
this.networkConnectionInfo = networkConnectionInfo;
}
public void AddParam(string key, string value)
{
if (Parameters == null)
{
Parameters = new Dictionary<string, string>();
}
Parameters.Add(key, value);
}
/// <summary>
/// Convenience method for a put request
/// </summary>
public string DoPut(string body, int timeOut)
{
return DoRequest(body, timeOut, HttpMethod.PUT);
}
/// <summary>
/// Performs a http request using the url assigned to this invoker
/// </summary>
///
/// <param name="body">
/// The request body
/// </param>
///
/// <param name="method">
/// The Http Method to Use
/// </param>
///
/// <param name="timeOut">
/// The time out for this request, in minutes
/// </param>
public string DoRequest(string body, int timeOut, HttpMethod method)
{
var result = "";
var url = BuildQueryString(serverUrl);
//submit request and get the response object
try
{
var req = (HttpWebRequest) WebRequest.Create(url);
req.Method = method.ToString();
req.AllowWriteStreamBuffering = true;
//make the request time out a little bit longer than expected
//to give the server time to timeout the request itself, which
//will give us more informative logging ie, we will be able to
//ascertain if the server has recieved the request but decided not to act on it
//this is because we set a TTL parameter on AUTD requests
req.Timeout = 1001*60*timeOut;
//retrieve request stream and wrap in streamwriter
Stream reqStream = req.GetRequestStream();
var wrtr = new StreamWriter(reqStream);
//Write message body to the request buffer
wrtr.Write(body);
wrtr.Close();
var resp = (HttpWebResponse) req.GetResponse();
result = ReadResponseStream(resp);
}
catch (WebException we)
{
var logger = FileLogManager.Instance;
if (!(url.Contains("AUTD") && we.Message == "The operation has timed out"))
{
logger.Debug(string.Format("{0}\r\n{1}", we.Status, we.Message));
}
else
{
logger.Debug("AUTD connection has timed out. Request Status: {0}, Exception Message: {1}", we.Status, we.Message);
}
var response = ReadResponseStream(we.Response);
if (!String.IsNullOrEmpty(response))
{
logger.Debug("exception response text: {0}", response);
}
}
catch (Exception e)
{
var logger = FileLogManager.Instance;
logger.Error("Error in request: " + e);
return "";
}
return result;
}
private string ReadResponseStream(WebResponse resp)
{
if (resp != null && resp.GetResponseStream() != null)
{
//retrieve the response stream and wrap in a streamreader
var respStream = resp.GetResponseStream();
var rdr = new StreamReader(respStream);
var result = "";
//read through the response line-by-line
var inLine = rdr.ReadLine();
while (inLine != null)
{
//process the response data
//for simplicity write to form list box
result += inLine + "\n";
inLine = rdr.ReadLine();
}
rdr.Close();
return result;
} else
{
return String.Empty;
}
}
private string BuildQueryString(string requestUrl)
{
//add query string if needed (?DEV=deviceId has already been added to the url in the invokerfactory)
if(Parameters != null)
{
foreach (KeyValuePair<string, string> pair in Parameters)
{
requestUrl += "&" + pair.Key + "=" + pair.Value;
}
}
return requestUrl;
}
public string ServerUrl
{
get
{
return serverUrl;
}
}
public string Url
{
get
{
return BuildQueryString(serverUrl);
}
}
public NetworkConnectionInfo NetworkConnectionInfo
{
get { return networkConnectionInfo; }
}
}
/// <summary>
/// Enum describing the possible http methods
/// </summary>
public enum HttpMethod
{
GET, POST, PUT, HEAD, DELETE, OPTIONS, TRACE, CONNECT
}
}
using System;
using System.Data;
using DDSGpsNav.Classes;
using DDSGpsNav.DB;
using DDSGpsNav.ServiceLayer.Invoker.MFData;
using DDSGpsNav.ServiceLayer.NetworkInfo;
using DDSGpsNav.ServiceLayer.UtilityClasses;
namespace DDSGpsNav.ServiceLayer
{
public class MasterFileManager
{
//since multiple threads could be running a MF Update at the same time. We keep track of
//the total number of MF updates in progress so we can ascertain if there are any mf updates
//in progress
protected static volatile int numberOfMFUpdateInProgress;
private const string truckIdOpParamFormat = "{0}][{1}";
private string TruckId;
public void UpdateMasterFileTable(MasterFileType mfType, OperationType opType, IMasterFileDAO dao)
{
var opParam = GetLastSyncUpdate(opType, dao);
UpdateMasterFileTable(mfType, opType, dao, opParam);
}
private static string GetLastSyncUpdate(OperationType opType, IMasterFileDAO dao)
{
var opParam = "0";
if(opType == OperationType.SYNC)
{
opParam = new MasterFileDAO().GetLastSyncUpdate(dao);
}
return opParam;
}
public void SyncForTruckId(MasterFileType mfType, IMasterFileDAO dao, string truckId)
{
//this is a kludgy hack to get around the fact that the dds service data structure
//was originally built so that it took either a last change date or a optional param
//yay for future proofing
TruckId = truckId; //global variable so it can be resent down the wire if there are more than 100 records
var opParam = string.Format(truckIdOpParamFormat, new MasterFileDAO().GetLastSyncUpdate(dao), truckId);
UpdateMasterFileTable(mfType, OperationType.SYNC, dao, opParam);
}
public void UpdateMasterFileTable(MasterFileType mfType, OperationType opType, IMasterFileDAO dao, string opParam)
{
numberOfMFUpdateInProgress++;
var connectionInfo = new NetworkMonitor().GetConnectionInfo();
if (connectionInfo.GetConnectionType() != ConnectionTypeEnum.NONE)
{
var mfDAO = new MasterFileDAO();
var results = new MasterFileUpdateCommand(mfType, opType, opParam).execute();
if (results != "[0x]\n" && opType == OperationType.TABLE && !(String.IsNullOrEmpty(results)))
{
mfDAO.ClearMFTable(dao.GetTableName());
}
var recordsRemaining = true;
while (results != "[0x]\n" && !String.IsNullOrEmpty(results) && recordsRemaining)
{
//The server will only send data in chunks of 100 (or thereabouts) records, so we don't
//overwhelm the RAM on our devices with huge datasets. If the server has decided to break
//this transmission into chunks, the first character of the result string that is returned
//will be a plus sign (+)
if (!(results.StartsWith("+"))) recordsRemaining = false;
char[] delims = { '[', ']' };
var dcns = results.Split(delims);
try
{
results = results.Substring(results.IndexOf('<'));
//escapes apostrophe characters (') in the response for insertion into a mysql database
results = results.Replace("'", @"\'");
var resultsSet = XmlUtils.DeserialiseDataSetFromXml(results);
var lastRecordId = dao.UpdateMFTable(resultsSet);
if (recordsRemaining && opType == OperationType.TABLE)
{
results = new MasterFileUpdateCommand(mfType, opType, lastRecordId).execute();
}
if(recordsRemaining && opType == OperationType.SYNC)
{
string newOpParam;
newOpParam = !string.IsNullOrEmpty(TruckId) ? string.Format(truckIdOpParamFormat, dcns[1], TruckId) : dcns[1];
results = new MasterFileUpdateCommand(mfType, opType, newOpParam).execute();
}
mfDAO.RecordLastSyncUpdate(dcns[1], dao);
}
catch (ArgumentOutOfRangeException)
{
//no xml returned, meaning there are no results for this MF update
FileLogManager.Instance.Info("No master file records for " + mfType.ToString() +
". Paramter was " + opParam);
}
}
// Remove any rows that are marked for removal in the master file database
if(opType == OperationType.SYNC)
{
mfDAO.FlushRemovedRecords(dao);
}
}
numberOfMFUpdateInProgress--;
}
public static bool MfUpdateInProgress
{
get {
return numberOfMFUpdateInProgress > 0;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment