For years I’ve been wanting to create a more user friendly I/O monitor application for the P control. If you’ve ever tried to quickly look up a signal on a machine you know what a pain in the neck it can be. If you don’t happen to have all of the signals memorized you either have to page through the slaves in physical mode or go find the electrical drawing. Oh, and don’t forget the search is case-sensitive. So I decided to make my own just for fun.
My Goals for the Project Are that the User Can:
- Search either comment or symbol name
- Search non case-sensitive
- Use it on any Okuma machine or off machine
- Carry it around on a memory stick (no installer)
- Add signals to a watch list
- Sort signals by type
- Save watch lists to an XML file (Chris Heeg’s idea)
Getting the Signal Info
There is no way to get the names of all of the signals on the Okuma through the API. there is also no way to read the status of an I/O signal by the symbol name. A generic set of I/O names won’t work because the mapping of the I/O signal addresses change from model to model. The around this problem is to use the IOTXTOUT.EXE tool to generate a CSV file.
Dim args As String = String.Format(” /L /B /File=”"{0}”"”, _tmpFilePath)
Dim txtIOProcess = Process.Start(“C:\OSP-P\TOOLS\IOTXTOUT.EXE”, args)
If Not txtIOProcess.WaitForExit(30000) Then
txtIOProcess.Kill()
Throw New TimeoutException(“Timeout waiting for IOFile generator”)
End If
txtIOProcess.Close()
This command will generate a CSV file with this format:
Date=20120928,Time=080854
ID=IX Logical Input
Addr,Bit,A,Msk,St,Label,Comment
0,0,,,0,ipNCST,NC start ..PB
0,1,,,1,ipNTS_B,NC feed hold/ .PB
As you can see the top of the file contains a timestamp followed by a section header, in this case inputs, followed by the column headers. The columns we care about are the Address(Addr), Bit, Logical/Physical(A) , Label and comment
Parsing the Data
Now that I had the data I wanted to find a way to quickly search this file to pull out the information needed. There are a few different ways I could have gone with this and I’m not sure this is the best but I decided to use Matt Perdeck’s cool LinqToCSV libray. This allowed me to write LINQ queries over the data in the CSV file.
Before I could use LINQ to query the data I had do clean up the CSV file. The first problem is that the file generated by the IOTXTOUT tool prints out a line for every potential I/O point not just the ones which are mapped. As a result at least 50% of the lines contain no useful data. As this will inevitably slow down any query I wanted to remove all of the blank data lines. The other problem is that the tool generates section titles and headers for each type of I/O so I also had to remove them as well. The LinqToCsv has no idea what to do with column headers which show up in the middle of the data.
Based on the columns in the CSV file I created a class which mapped the columns of the CSV File using Property Attriubtes.
<CsvColumn(Name:=”Addr”, FieldIndex:=1, canBeNull:=True)>
Public Property Address As Integer?
…
I then created a CsvFileDescription which describes the CSV file:
_inputFileDescription = New CsvFileDescription() With {.SeparatorChar = “,”, .FirstLineHasColumnNames = False, .EnforceCsvColumnAttribute = True}
With this in place I was able to read the data by passing in a path to the clean CSV file along with my description object
rtnIoList = _OkIoContext.Read(Of OkumaIOPointInfo)(FilePath, _inputFileDescription)
Now after all this the the end result is that we can do queries like this on our data:
Dim InputsThatContainDoor = From iop In _IOList
Where iop.Comment.Contains(“Door”) AndAlso iop.IOType = 0
Select iop
Making it Machine Type Neutral
To make the application machine type neutral, meaning it can run on any Okuma machine, I implemented the factory pattern I described in previous post.
The interface is very simple. It describes a function to get the status of an I/O signal, a function to get the machine type and a sub to handle closing.
Public Interface Iokuma
Function GetIoBitStatus(Signal As OkumaIOPointInfo) As Boolean
Function GetMachineType() As enumMachineType
Sub close()
End Interface
I have 3 classes which implement the IOkuma interface. One for a mill, one for a lathe and the other for off machine simulation. This allows me to program against the IOkuma interface on the MainWindow rather than creating a new Window for each of the three scenarios.
WPF Databinding
The only other somewhat interesting about the application is how I was able to use WPF’s Databinding to display the I/O status.
In Visual Studio 2010 if you add an “Object” data source and point it at a class in your project you end up with a CollectionViewSource in the Window.Resources. I bound the ItemsSource of a ListBox to this collection. I then used a User Control (uc_IoPoint) to display the name and the status of the signal in the Listbox.
<ListBox x:Name=”lbIO” HorizontalAlignment=”Left” Width=”393.183″ ItemsSource=”{Binding}” Margin=”0,0,10,5″ VirtualizingStackPanel.IsVirtualizing=”True” SelectionMode=”Multiple”>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<local:uc_IoPoint Margin=”5″/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Making it Portable
I attempted to use the Costura VS addin to embedded all of the required Dll’s into the exe. For some reason the Okuma Dll’s refuse to be loaded from via stream. If anyone has any insight on this I would be very grateful (test project). I would eventually like to get it down to a single file executable. For now it is the Okuma IO.exe and the Okuma DLL files.