Aeries PIC Table – Saving Student Pictures with C#

Eagle_Software-Aeries_SIS2

If you are a school district using the Aeries PIC table for saving student pictures, you may have the need to extract pictures for use in other applications.

As districts switch over to use the “new” PIC table, this will become especially important if your district has a site that may update it’s pictures frequently or has a large turnover of students and new pictures are added throughout the year.
 
Read more

.NET 4.5 Task Based Console Application with Progress

Lets say you need to create a simple .NET 4.5 application for processing some data… The quickest way to do that is to create a new Console Application in Microsoft Visual Studio 2012. I’m assuming that you already know how to do all of that… If not, you can Google it!

Once you have that, the first thing we will do is start out with your work process, this will be the actual Task that does all of the processing. We’re going to use the Task in the most basic fashion, so if you need to do more with it, take a look at System.Threading.Task on MSDN.

Here is the initial code to get you started; a basic entry point that calls a task and then waits for it to complete.

static Task DoWorkAsync()
{
	return Task.Factory.StartNew(() => {
			//Do Some Work...
			Thread.Sleep(250);
		}
	});
}
static void Main(string[] args)
{
	// Start the Task
	Task t = DoWorkAsync();

	// Wait for the task to complete
	t.Wait();
}

With that code, you will get a simple application that will spin off a new thread that will run the code in the DoWorkAsync function. We call t.Wait() in the main thread so that the application doesn’t terminate before the task completes.

That is all it basically takes to write a simple Task based console application in .NET 4.5. But just for the sake of fun, lets add some more detailed processing and some way of reporting back some level of progress.

public static int spinnerPos = 0;
public delegate void UpdateProgressDelegate(float pctComplete);
public static UpdateProgressDelegate UpdateProgress = (float pctComplete) => {
	if (pctComplete >= 100f) {
		Console.Write("\rProcess Complete!".PadRight(Console.BufferWidth));
	}
	else {
		char[] spinner = new char[] { '-', '\\', '|', '/' };
		Console.Write(string.Format("\rWorking... {0} - {1:0.0}%", spinner[spinnerPos], pctComplete).PadRight(Console.BufferWidth));
		spinnerPos = (spinnerPos >= 3) ? 0 : spinnerPos + 1;
	}
};

static Task DoWorkAsync()
{
	return Task.Factory.StartNew(() => {

		// Show the starting progress
		UpdateProgress(0f);

		int loopCount = 15;
		for (int i = 0; i < loopCount; i++) {

			Thread.Sleep(250);

			// Update the progress
			float pctComplete = (((float)(i + 1) / (float)loopCount) * 100f);
			UpdateProgress(pctComplete);
		}
	});
}

static void Main(string[] args)
{
	// Start the Task
	Task t = DoWorkAsync();

	// Wait for the task to complete
	t.Wait();

	Console.Write("\r\n\r\nDone!");
	Console.ReadLine();
}

With these updates the application will report back some basic progress to the console using a simple delegate.

Take the code and play around with it; it’s a good base for any long running process in a console application.

IP Adress Change Notifications

I’ve always had some sort of server at my house, for various reasons, but one thing that I never have to go along with those servers is a static IP address. Either they are way too expensive and not worth it for something that I am not making any money from, or they are completely unavailable. One day, hopefully my ISP will provide them for something other than a huge monthly business package, but that is probably wishful thinking.

Because I have servers, and this dynamic IP address, every once in a while my IP address changes. It’s much less frequent than I thought it would be, but it still happens from time to time. And since it doesn’t happen often, it’s usually one of those things I don’t notice until I am away from home and trying to access something on one of my servers, with no way to check the new IP address. I also try to keep some DNS records updated with the address, for easy reference.

So, my solution was to make a simple WebAPI service that simply returns the clients IP address, and then to have a simple application that consumes that service and keeps track of the addresses that are returned. Once a change is found, it will shoot out an e-mail to me with the new address and a reference to the old address. Then I can update my DNS, etc… No more unknown address changes. And if it changes while I’m out of the house, I will still be able to get access to my systems.

The WebAPI is very simple, just a Get() method with some very simple IP logic in it.

public class IpPingController : ApiController
{
	public string Get()
	{
		string ipAddr = HttpContext.Current.Request.UserHostAddress;
		if(string.IsNullOrEmpty(ipAddr)) {
			object property;
			Request.Properties.TryGetValue(typeof(RemoteEndpointMessageProperty).FullName, out property);
			RemoteEndpointMessageProperty remoteProperty = property as RemoteEndpointMessageProperty;
			if (remoteProperty != null) {
				ipAddr = remoteProperty.Address;
			}
		}
		return ipAddr;
	}
}

You can test this service by hitting this address in your browser, http://www.santsys.com/api/ipping/.

The application is a very simple console application that I run using windows scheduled tasks. I have it setup to run every 2 hours.

private static Configuration _cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
public static Configuration Config { get { return _cfg; } }
public static string ApiAddress { get { return ConfigurationManager.AppSettings["IpPingAddress"]; } }
public static string PreviousIpAddress
{
	get { return ConfigurationManager.AppSettings["PreviousIpAddress"]; }
	set { _cfg.AppSettings.Settings["PreviousIpAddress"].Value = value; }
}
public static string LastUpdate
{
	get { return ConfigurationManager.AppSettings["LastCheck"]; }
	set { _cfg.AppSettings.Settings["LastCheck"].Value = value; }
}
public static string SmtpHost { get { return ConfigurationManager.AppSettings["SmtpHost"]; } }
public static int SmtpPort
{
	get
	{
		string port = ConfigurationManager.AppSettings["SmtpPort"];
		int iPort;
		if (int.TryParse(port, out iPort)) {
			return iPort;
		}
		return 25;
	}
}
public static string SmtpUser { get { return ConfigurationManager.AppSettings["SmtpUser"]; } }
public static string SmtpPassword { get { return ConfigurationManager.AppSettings["SmtpPassword"]; } }
public static string SmtpFrom { get { return ConfigurationManager.AppSettings["SmtpFrom"]; } }
public static string SmtpTo { get { return ConfigurationManager.AppSettings["SmtpTo"]; } }

static void Main(string[] args)
{
	try {
		DoWork();

#if DEBUG
		Console.Write("\r\nPress any key to exit.");
		Console.ReadKey();
#endif
	}
	catch (Exception ex) {
		Console.WriteLine("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace);
	}
}

public static void DoWork()
{
	Version ver = Assembly.GetAssembly(typeof(Program)).GetName().Version;
	Console.WriteLine("IpPing v{0}.{1} build {2}", ver.Major, ver.Minor, ver.Revision);
	Console.WriteLine("\r\nLast Run: {0} @ {1}", PreviousIpAddress, LastUpdate);
	Console.Write("\r\n");

	if (string.IsNullOrEmpty(ApiAddress)) {
		Console.WriteLine("Error: No Api Address found in configuration. (IpPingAddress)");
		return;
	}

	HttpClient client = new HttpClient();
	client.BaseAddress = new Uri(ApiAddress);

	// Add an Accept header for JSON format.
	client.DefaultRequestHeaders.Accept.Add(
			new MediaTypeWithQualityHeaderValue("application/json"));

	HttpResponseMessage response = client.GetAsync("api/IpPing/").Result;
	if (response.IsSuccessStatusCode) {
		var ipAddrInfo = response.Content.ReadAsAsync<string>().Result;

		Task emailUpdate = null;

		// if the IP Address has not changed
		if (PreviousIpAddress != ipAddrInfo) {
			Console.WriteLine("New IP address detected.\r\n\tOld Address:{0}\r\n\tNew Address:{1}\r\n\r\nSending notification.", PreviousIpAddress, ipAddrInfo);
			emailUpdate = SendUpdateEmail(ipAddrInfo, PreviousIpAddress);
			PreviousIpAddress = ipAddrInfo;
		}

		LastUpdate = DateTime.Now.ToString();
		Config.Save(ConfigurationSaveMode.Modified);
		ConfigurationManager.RefreshSection("appSettings");
		
		// wait for the email to send, if one is being sent
		if (emailUpdate != null) {
			emailUpdate.Wait();
		}
	}
	else {
		Console.WriteLine("Error: {0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
	}
}

public static Task SendUpdateEmail(string newIp, string oldIp)
{
	return Task.Factory.StartNew(() => {
		try {
			SmtpClient smtpClient = new SmtpClient(SmtpHost, SmtpPort);
			if (!string.IsNullOrEmpty(SmtpUser) && !string.IsNullOrEmpty(SmtpPassword)) {
				smtpClient.UseDefaultCredentials = false;
				smtpClient.Credentials = new NetworkCredential(SmtpUser, SmtpPassword);
			}
			MailMessage msg = new MailMessage(SmtpFrom, SmtpTo);
			msg.IsBodyHtml = true;
			msg.Priority = MailPriority.High;
			msg.Headers.Add("X-IP-PING", "Automated message from IP PING");
			msg.Headers.Add("X-IP-PING-ADDRESS", newIp);
			msg.Subject = "Ip Address Changed!";
			msg.Body =
					"<style type=\"text/css\">body, p { color: #555; font-size: 12px; font-family: Arial; } a, a:link, a:visited { color: #486db5; text-decoration:none;}</style>"
				+ "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">This is a notification from Ip Ping. Your IP Address has changed.</p>"
				+ "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">Your new IP Address is <b style=\"font-size: 14px; color: #000;\">" + newIp + "</b></p>"
				+ "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">Your old IP Address was " + (string.IsNullOrEmpty(oldIp) ? "(blank)" : oldIp) + "</p>"
				+ "<br /><br /><p style=\"color: #666;font-size: 10px; font-family: Arial;\">© " + DateTime.Now.Year.ToString() + " Santomieri Systems - <a href=\"http://www.santsys.com/\">www.santsys.com</a></p>";

			smtpClient.Send(msg);
		}
		catch (Exception ex) {
			Console.WriteLine("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace);
		}
	});
}

That’s pretty much it. Feel free to use this if it will help you out. I know it solved a lot of issues for me.

RFC 4648 Base32 Encoding

I have been playing around with writing some basic encoders, figuring a good first step would to start with something on the easier side, I decided to jump in with a Base32 encoder.

First things first, here are some details on Base32 from RFC 4648, Section 6.

 

The Base32 encoding is designed to represent arbitrary sequences of octets in a form that needs to be case insensitive but that need not be human readable.

A 33-character subset of US-ASCII is used, enabling 5 bits to be represented per printable character. (The extra 33rd character, “=”, is used to signify a special processing function.) The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters. Proceeding from left to right, a 40-bit input group is formed by concatenating 5 8bit input groups. These 40 bits are then treated as 8 concatenated 5-bit groups, each of which is translated into a single character in the base 32 alphabet. When a bit stream is encoded via the base 32 encoding, the bit stream must be presumed to be ordered with the most-significant- bit first. That is, the first bit in the stream will be the high-order bit in the first 8-bit byte, the eighth bit will be the low- order bit in the first 8bit byte, and so on. Through the development process, I came up with 3 basic designs for the process.

Each 5-bit group is used as an index into an array of 32 printable characters. The character referenced by the index is placed in the output string. These characters, identified in Table 3, below, are selected from US-ASCII digits and uppercase letters.

In my tinkering around with this, I came up with a couple of options, some quick and dirty, and some a
little more in-depth. I’ll outline each of the methods that I used, and surprisingly they all seemed to perform within about a 1/1000th of a second (in some very basic performance testing that I did).

The first method that I tired was basically a simple method that took 1 bit at a time and shifted it into a buffer. This method, simply put, builds the 5-bit blocks bit-by-bit.

public static string ToBase32String(byte[] bytes) {
	 if (bytes == null || bytes.Length < 1) return string.Empty;
	
	 StringBuilder sb = new StringBuilder();
	 byte index = 0;
	 int hi = 0;
	 int currentByte = 0;
	 int totalBytes = bytes.Length;
	
	 byte workingByte = bytes[currentByte];
	 int bits = 0;
	 index = 0;
	 
	 while (true) {
		index = (byte)((index << 1) | ((workingByte >> (7 - bits)) & 1));
		bits++;
		hi++;
		
		if (hi == 5) {
			if (index == 0 && currentByte >= totalBytes) { index = 32; }
			sb.Append(ValidRFC4648Chars[index]);
			if (index == 32 && ((sb.Length > 8 && (sb.Length % 8) == 0) || (sb.Length == 8))) { break; }
			index = 0;
			hi = 0;
		}
	
		if (bits == 8) {
			currentByte++;
			bits = 0;
			if (currentByte >= totalBytes) {
				workingByte = 0;
				if ((sb.Length % 8) == 0 && sb.Length >= 8) { break; }
			}
			else {
				workingByte = bytes[currentByte];
			}
		}
	}
	
	 if (sb.Length < 8) { return sb.ToString().PadRight(8, '='); }

	 return sb.ToString(); 
}

 

The next method that I tired, was the simplest method. I say simplest because it uses the most built in .Net stuff and doesn’t do as much binary arithmetic and bit shifting. I created a bit string (base-2 number string) using Convert.ToString(byte, 2) then I pull 5-bit (in this case, 5-character) chunks from the string and then encode them.

public static string ToRFC4648Base32_Strings(byte[] bytes) {
	 StringBuilder sb = new StringBuilder();
	 int pos = 0;
	 byte index = 0;
	 StringBuilder byteString = new StringBuilder();
	
	 foreach (byte b in bytes) {
		byteString.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
	 }
	
	 string bString = byteString.ToString();
	
	 while (pos < (byteString.Length / 5)) {
		index = Convert.ToByte(bString.Substring(pos * 5, 5), 2);
		pos++;
		sb.Append(ValidRFC4648Chars[index]);
	 }
	
	 if ((pos * 5) < byteString.Length) {
		index = Convert.ToByte(bString.Substring(pos * 5).PadRight(5, '0'), 2);
		sb.Append(ValidRFC4648Chars[index]);
	 }
	
	 while ((sb.Length < 8) || (sb.Length > 8 && sb.Length % 8 != 0)) {
		sb.Append("=");
	 }

	 return sb.ToString(); 
} 

The final method, the one that I like the best, pulls the data out in a more block based manner and really just accomplishes the task.

public static string ToRFC4648Base32(byte[] bytes) {
	if (bytes == null || bytes.Length < 1) return string.Empty;

	StringBuilder sb = new StringBuilder();
	int currentByte = 0;
	int start = 0;
	int stop = 5;
	byte workByte = 0;
	byte index = 0;

	int numBytes = bytes.Length;

	while (currentByte < numBytes || (sb.Length < 8 || (sb.Length > 8 && sb.Length % 8 != 0))) {
		if (start == 0) {
		workByte = (byte)(bytes[currentByte] >> 3);
		start += 5;
		stop += 5;
	}
	else {
		if (currentByte < numBytes) {
			workByte = bytes[currentByte];
		}
		else {
			workByte = 0;
		}

		workByte = (byte)(((workByte << start) & 0xff) >> 3);
		byte nextByte = 0;
		if (stop > 8) {
			if (currentByte < numBytes - 1) {
				nextByte = bytes[++currentByte];
			}
			else {
				currentByte++;
			}
			workByte = (byte)(workByte | (nextByte >> (16 - stop)));
			start -= 3;
			stop -= 3;
		}
		else {
			if (currentByte < numBytes) {
				nextByte = bytes[currentByte];
			}
			else {
				nextByte = 0;
			}
			workByte = (byte)(workByte | ((nextByte & (((1 << (8 - stop)) - 1) << (8 - stop)))));
			start += 5;
			stop += 5;
		}
	}

	if (workByte == 0 && currentByte == numBytes && (numBytes % 5) == 0) break;
	index = workByte;

	if (index == 0 && currentByte >= numBytes) index = 32;
		sb.Append(ValidRFC4648Chars[index]);
	}

	return sb.ToString(); 
}

From the limited testing I did, the latter code performed an average encoding speed of 103,057,258-bit/s. For the testing I encoded 5000 blocks of 32-bytes of random data. On average it took 0.0124028 seconds to perform those encodings. Your results, and math may vary.

With all of these, I’m sure there are many things that could be done to improve performance, but for my playing around they perform perfectly well. Feel free to use and update as you wish!