Thursday, 15 July 2010

.NET SerialPort Woes - Zach Saw's Blog

.NET SerialPort Woes - Zach Saw's Blog

Very interesting post regarding a potential bug in .NET serial port implementation.

Monday, 12 April 2010

Get name of current Windows user

This function returns a string of the currently logged in user on Windows. An empty string is returned if a valid name cannot be obtained.

   1:  /// <summary>
   2:  /// Returns the name of the currently logged on Windows user.
   3:  /// </summary>
   4:  /// <returns>
   5:  /// Current user logon name string. Returns an empty string if a valid name cannot be obtained.</returns>
   6:  private static string GetWindowsUserName()
   7:  {
   8:      System.Security.Principal.WindowsIdentity win_id = null;
   9:      string userName = String.Empty;
  10:   
  11:      try
  12:      {
  13:          // Get an object with details about the currently logged on Windows user.
  14:          win_id = System.Security.Principal.WindowsIdentity.GetCurrent();
  15:   
  16:          // Get current user domain name and logon name strings.
  17:          // [0] = Domain name.
  18:          // [1] = Logon name.
  19:          string[] names = win_id.Name.Split(new Char[] { '\\' }); // win_id.Name returns "Domain\Logon"
  20:   
  21:          if (names.Length == 2) { userName = (names[1]); }
  22:      }
  23:      catch (System.Security.SecurityException)
  24:      {
  25:          // Retrieving Windows user details failed so ensure returned name is an empty string.
  26:          userName = String.Empty;
  27:      }
  28:      finally
  29:      {
  30:          // Dispose of windows identity object.
  31:          if (win_id != null) { win_id.Dispose(); }
  32:      }
  33:   
  34:      return userName;
  35:  }

Sunday, 7 March 2010

Google Reader - Too USA obsessed.

I have been using Google's Reader application for a while now and have had no problems. It's very easy to use, it does all I need it to do and everything is ok in general. One thing bothers me though, and that is the recommendations feature. As I understand it, this should present selections that it think may be of interest based upon things that you select as 'Like' and possibly your browsing activity. Maybe other criteria too, I don't know.

Despite this, almost everything that is presented to me in 'Recommendations' is American based. American news, American sports, American TV, American bloody Idol, yada, yada, yada.

Now I don't have anything against Americans, but there IS a world outside the good ol' US of A.

Monday, 22 February 2010

ASUS Eee PC 1201N netbook. Why are we waiting?

The potentially mould-breaking ASUS Eee PC 1201N netbook was released in the US during November (or possibly even October) which allowed a lucky few living on the other side of the pond to find one under the Christmas tree.

Fine, ASUS have to roll out their shipments and as I assume that the US is the largest market, it is only natural that they come first. As for the UK, an initial availability date was set for January. Indeed, Amazon UK listed this netbook on the 6th January with an initial delivery estimate of '1 to 3 months'.

Yet here we are on the brink of March and they are still as rare as rocking horse s**t, with Amazon STILL quoting '1 to 3 months' estimated delivery. Quite why the estimate cannot be any more accurate than that escapes me.

Come on ASUS, pull your finger out. Why does it take FOUR MONTHS (at least) longer to release it over here?

UPDATE 01/03/2010:
Well, the first of March is here, and still no sign of this netbook appearing in the UK. In fact, a review of various online sellers show that shipping dates are slipping even further, and yup, Amazon still shows '1 to 3 months'. Unbelievable.

UPDATE 26/03/2010:
Finally, FINALLY got this delivered after much shenanigans with Amazon UK's delivery agent HDNL (Home Delivery Network Limited.) Tales of woe include 'unable to find address', 'delivery van breakdown' and 'unable to contact driver'. I was almost too scared to open the box in case I found something else entirely within it. However, it is all present and correct.


Monday, 17 November 2008

BCD to Binary conversion

I needed to convert some numbers received from a PLC from their original packed BCD format to binary (hex) format. The numbers to convert are split into two 16-bit halves, and need to be converted into one 32-bit number.

(Packed BCD values contain two digits for every byte.)

There is a method that exists that will convert numbers from BCD into a true hex number, by using string formatting, but this is relatively expensive in terms of performance and I required the best performance possible.

So, I came up with this function:

   1:  /// <summary>
   2:  /// Convert two PLC words in BCD format (forming 8 digit number) into single binary integer.
   3:  /// e.g. If Lower = 0x5678 and Upper = 0x1234, then Return is 12345678 decimal, or 0xbc614e.
   4:  /// </summary>
   5:  /// <param name="lower">Least significant 16 bits.</param>
   6:  /// <param name="upper">Most significant 16 bits.</param>
   7:  /// <returns>32 bit unsigned integer.</returns>
   8:  /// <remarks>If the parameters supplied are invalid, returns zero.</remarks>
   9:  private uint BCD2ToBin(uint lower, uint upper)
  10:  {
  11:      uint binVal = 0;
  12:   
  13:      if ((lower | upper) != 0)
  14:      {
  15:          int shift = 0;
  16:          uint multiplier = 1;
  17:          uint bcdVal = (upper << 16) | lower;
  18:   
  19:          for (int i = 0; i < 8; i++)
  20:          {
  21:              uint digit = (bcdVal >> shift) & 0xf;
  22:   
  23:              if (digit > 9)
  24:              {
  25:                  binVal = 0;
  26:                  break;
  27:              }
  28:              else
  29:              {
  30:                  binVal += digit * multiplier;
  31:                  shift += 4;
  32:                  multiplier *= 10;
  33:              }
  34:          }
  35:      }
  36:   
  37:      return binVal;
  38:  }


The function takes in two unsigned itegers that hold each of the 16-bit weighted BCD values, and returns a 32-bit unsigned integer.

It is important to note that the return is an unsigned integer, as this allows BCD values up to 0x99999999 to be converted correctly.

I have also taken the approach of returning zero if any of the values supplied are incorrect, i.e. contain an invalid digit. (BCD values can only contain digits ranging between 0 and 9, and not A to F.)

Of course, it may be desirable to return a different value, or to throw an exception if the parameters supplied are invalid, but for my purposes, returning a zero was sufficient.

This method proved to be around four to five times quicker than by using the string format method.

Friday, 24 October 2008

Using RowPrePaint event of a DataGridView control

To draw a row in a DataGridView control in a different style depending on a certain condition, you can use the RowPrePaint event to interrupt the painting of the rows in the grid.

You then have a choice of which parts of the row to be painted automatically and which parts to paint yourself via the PaintParts event property.

In the code below, I paint a row with a custom brush, depending on the value of a column that is named 'Error'.

   1:  /// <summary>
   2:  /// Handles the drawing of each DataGridView row depending on set log error state.
   3:  /// </summary>
   4:  /// <param name="sender"></param>
   5:  /// <param name="e"></param>
   6:  private void setLogDataGridView_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
   7:  {
   8:      // Paint all parts of row except SelectionBackground and the Focus box.
   9:      e.PaintParts = DataGridViewPaintParts.All & ~(DataGridViewPaintParts.SelectionBackground | DataGridViewPaintParts.Focus);
  10:   
  11:      // Get the value of the error column of the current row
  12:      string val = this.setLogDataGridView.Rows[e.RowIndex].Cells["Error"].Value.ToString();
  13:   
  14:      if (val != "0")
  15:      {
  16:           // Get rectangle of area to paint with custom brush.
  17:            Rectangle rowBounds = new Rectangle(
  18:             this.setLogDataGridView.RowHeadersWidth, e.RowBounds.Top,
  19:             this.setLogDataGridView.Columns.GetColumnsWidth(
  20:             DataGridViewElementStates.Visible) -
  21:             this.setLogDataGridView.HorizontalScrollingOffset + 1,
  22:             e.RowBounds.Height);
  23:   
  24:      // Disable normal painting of backgrounds.
  25:      e.PaintParts &= ~(DataGridViewPaintParts.Background | DataGridViewPaintParts.ContentBackground);
  26:   
  27:      e.PaintHeader(false);
  28:   
  29:      // Paint the custom background.
  30:      using (Brush lgb = new LinearGradientBrush(rowBounds, Color.Red,
  31:             Color.DarkRed, LinearGradientMode.Vertical))
  32:      {
  33:          e.Graphics.FillRectangle(lgb, rowBounds);
  34:      }
  35:   
  36:      // Set the text colour.
  37:      this.setLogDataGridView.Rows[e.RowIndex].DefaultCellStyle.ForeColor = Color.Yellow;
  38:      this.setLogDataGridView.Rows[e.RowIndex].DefaultCellStyle.SelectionForeColor =      Color.Yellow;
  39:      }
  40:  }

Create a User business object

To create a business object that represents a 'User' of my Datalog application, I have used the code shown below, and it works well for my purposes. I decided to make it a Struct instead of a Class as it is a small and lightweight object. If any readers think that it should preferably be a Class, then please leave a comment. I'm still learning this stuff, so any tips welcome!

   1:  using System;
   2:   
   3:  namespace KAS.Datalog
   4:  {
   5:      /// <summary>
   6:      /// Represents a user of the application.
   7:      /// </summary>
   8:      public struct User
   9:      {
  10:          #region Fields
  11:   
  12:          private string _strName;
  13:          private UserLevel _userLevel;
  14:   
  15:          #endregion
  16:   
  17:          #region Properties
  18:   
  19:          /// <summary>
  20:          /// Gets or sets the user name.
  21:          /// </summary>
  22:          public string Name
  23:          {
  24:              get { return this._strName; }
  25:              set { this._strName = value; }
  26:          }
  27:   
  28:          /// <summary>
  29:          /// Gets or sets the user level.
  30:          /// </summary>
  31:          public UserLevel Level
  32:          {
  33:              get { return this._userLevel; }
  34:              set { this._userLevel = value; }
  35:          }
  36:   
  37:          #endregion
  38:   
  39:          #region Constructor
  40:   
  41:          /// <summary>
  42:          /// Constructor
  43:          /// </summary>
  44:          /// <param name="name">Name of the user</param>
  45:          /// <param name="level">Secutirty level</param>
  46:          public User(string name, UserLevel level)
  47:          {
  48:              if (name != "") { this._strName = name; }
  49:              else { this._strName = "Unknown"; }
  50:   
  51:              this._userLevel = level;
  52:          }
  53:   
  54:          #endregion
  55:   
  56:          #region Overridden methods
  57:   
  58:          public override string ToString()
  59:          {
  60:              return this._strName + "," + this._userLevel.ToString();
  61:          }
  62:   
  63:          #endregion
  64:      }
  65:  }

Friday, 25 July 2008

Toshiba Inverters

I am currently using inverters from Toshiba, namely the VF-nC1S and VF-S11 models.

Petty normal inverters when all is said and done, but with a couple of big advantages for the applications I am working with:

1. 110V single phase input models.
This is a big plus, since they still provide 220V three phase motor control and it allows use on U.S supplies (and others) that use 110V 1-ph. It also means that all machine units can be built and wired with a standard motor, with only the inverter needing to be changed depending on the shipping location. No 110V to 220V step-up transformers!

2. Three speed control inputs.
Three inputs as standard on the VF-S11, and by configuring a multi-use input on the VF-nC1S allows for eight distinct binary selected frequency selections. Sixteen can be selected by configuring a fourth input.



The VF-S11 models are slightly higher spec in that they have more functions and control I/O at your disposal.

A drawback of the 110V models is the lack of a built-in EMC filter, so an external one is required.

Also, the particular VF-nC1S model I am currently using - the 0.4kW model - has a relatively high earth leakage current. This is stated as 11mA in the specs, and measured at around 6-10mA depending on load. This can prove a problem if multiple units are powered from the same supply due to nuisance ELCB tripping, and the requiremnet to use a much more stringent earthing policy depending on the arrangement of units.

Tuesday, 22 July 2008

Omron PLC Dummy I/O

Using dummy IO modules on an Omron PLC with a 'virtual backplane' (i.e. a CJ1M for example) is a good way to keep addresses used for varying projects the same, and adding extra I/O when needed for extra functions, without upsetting the existing addressing structure.

For example, suppose you have a machine process that requires three 16 way input cards, and three 16 way output cards. These could of course occupy address space 000, 001, 002, 003, 004 and 005 without problem. But then suppose their is a need for a similar process with some limited functionality that only required two input and output modules. Once again, these could occupy addresses 000, 001, 002 and 003, but these now conflict with the earlier allocation and could cause confusion when wiring and producing documentation, because where 'Solenoid 1' connected to output 003.00 previously, it now connects to output 002.00.

A solution to this is to insert some 'dummy' I/O modules into the I/O table to act as placeholders and to allow the addressing to remain consistent between the two processes. ('Dummy' I/O modules are not actual physical modules, but rather they are virtual.)

The picture below show an I/O table with some dummy modules inserted:



Dummy I/O modules of varying sizes can be added by selecting them from the Basic IO menu of the 'Add unit' dropdown menu.

Sunday, 20 July 2008

The Moeller Wiring Manual

The Moeller Wiring Manual is an excellent resource for any electrical controls engineer.

Especially useful for the 'Specifications, Formulae, Tables' section.