Introduction
In our company, we use OMRON PLC to manage our production machines. Initially, we had a SCADA application to obtain data stored in the PLC. The truth is that I wanted a simple solution that allows me to create Windows services to get data from PLC, instead of using a dedicated PC for this task. I tried CX-Server Lite, but its components require a WindowsForm application, and cannot be used in Windows services. This is the reason why I decided to write this class.
The library consists of a main class, a transport layer class, currently for TCP, and one class to manage the 'FINS' orders via TCP. The transport and orders layer are based on two interfaces, ITransport
, IFINSCommand
. This will easily allow to add more functionality (UDP, Serial, ...).
Background
This class uses synchronous sockets to communicate with the PLC. Keep in mind that a communication issue could block the current thread. I think it is advisable to use a secondary thread to avoid this problem. As you can see, this class does not launch message box windows neither console messages, most functions return bool
values and you should use .LastError()
function to check the result.
The current version implements 3 PLC messages:
- [1,1] MEMORY AREA READ
finsMemoryAreaRead()
- [1,2] MEMORY AREA WRITE
finsMemoryAreaWrite()
- [5,1] CONTROLLER DATA READ
finsConnectionDataRead()
And some methods to deal with DM area:
ReadDM()
ReadDMs()
WriteDM()
ClearDMs()
And two new methods to deal with CIO Bit area:
ReadCIOBit()
WriteCIOBit()
Take a look at tcpFINSCommand.cs and you'll notice that it's really easy to add new methods for another PLC message.
Using the Code
First and foremost, add a reference for the mc.Omron.vx.xx.dll to your project.
Add one mcOMRON.OmronPLC
object to your project and initialize it using one transport type (only TCP is available by now):
public partial class TestPLC : Form
{
mcOMRON.OmronPLC <var>plc;
public TestPLC()
{
InitializeComponent();
this.plc = new mcOMRON.OmronPLC(mcOMRON.TransportType.Tcp);
}
...
Before you Connect()
with the PLC, you must set up the TCP connection parameters. As the OmronPlc
uses the interface class, you must cast it to the corresponding tcpFINSCommand
class and then call SetTCPParams
method.
private void Connect()
{
if (ip.Text == "") return;
if (port.Text == "") return;
try
{
mcOMRON.tcpFINSCommand tcpCommand = ((mcOMRON.tcpFINSCommand)plc.FinsCommand);
tcpCommand.SetTCPParams(IPAddress.Parse(ip.Text), Convert.ToInt32(port.Text));
if (! plc.Connect())
{
throw new Exception(plc.LastError);
}
...
By default, this class uses Byte
, UInt16
and UInt32
variables, and arrays are passed by ref.
private void ReadDM()
{
if (dm_position.Text == "") return;
UInt16 dmval=0;
try
{
if (! plc.ReadDM(Convert.ToUInt16(dm_position.Text), ref dmval))
{
throw new Exception(plc.LastError);
}
dm_value.Text = dmval.ToString();
dialog.Text = plc.LastDialog("READ DM");
dialog.AppendText("DM VALUE: " + dmval.ToString());
}
catch (Exception ex)
{
MessageBox.Show("ReadDM() Error: " + ex.Message);
}
}
As you can see in the screenshot, I've added a debug functionality. If you call LastDialog()
after sending any command, you can get the dialog between PC and PLC in hexadecimal format.
Points of Interest
Would you like to add a handler for new PLC message?
Let me show you the actual method: MemoryAreaWrite()
public bool MemoryAreaWrite(MemoryArea area, UInt16 address, UInt16 count, ref Byte [] data)
{
try
{
this.MC = 0x01;
this.SC = 0x02;
this.cmdFins[F_PARAM] = (Byte)area;
this.cmdFins[F_PARAM + 1] = (Byte)((address >> 8) & 0xFF);
this.cmdFins[F_PARAM + 2] = (Byte)(address & 0xFF);
this.cmdFins[F_PARAM + 3] = (Byte)(0);
this.cmdFins[F_PARAM + 4] = (Byte)((count >> 8) & 0xFF);
this.cmdFins[F_PARAM + 5] = (Byte)(count & 0xFF);
this.finsCommandLen = 18;
return FrameSend(data);
}
catch (Exception ex)
{
this._lastError = ex.Message;
return false;
}
}
You must set Main code and Subcode properties and use cmdFins[]
array to set additional parameters. Finally, set the (finsCommandLen
) command length and call FrameSend()
to send the message.
History
October 2016
- There was a bug in
MemoryArea
values, I was using wrong values. - I've added specific funtions for signed values.
November 2016
- I've added some functions in
BTool
class to work with Bits (IsBitSet
, SetBit
, UnsetBit
). - I've added two methods to access CIO Bit area.
This is it for now.
Please feel free to contact me if you have any questions about this class.