Skip to content

Commit

Permalink
IEC61850client: changed SBO commands to Select With Value for SBO con…
Browse files Browse the repository at this point in the history
…trol modes and kconv1=1.
riclolsen committed Nov 15, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 5403708 commit 73028a5
Showing 3 changed files with 140 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -49,6 +49,7 @@ public class Iec61850Control
public string js_cmd_tag; // tag name on json scada
public double value; // command value
public string fc; // command fc
public bool useSelectWithValue; // will use select with value when kconv1=1 on point_list and SBO control mode
public DateTime timestamp; // timestamp
public Iec61850Entry iecEntry; // iec61850 object entry
}
2 changes: 1 addition & 1 deletion src/libiec61850/dotnet/core/2.0/iec61850_client/Main.cs
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ partial class MainClass
{
public static String CopyrightMessage = "{json:scada} IEC61850 Client Driver - Copyright 2023 Ricardo Olsen";
public static String ProtocolDriverName = "IEC61850";
public static String DriverVersion = "0.1.3";
public static String DriverVersion = "0.1.4";
public static bool Active = false; // indicates this driver instance is the active node in the moment
public static Int32 DataBufferLimit = 20000; // limit to start dequeuing and discarding data from the acquisition buffer
public static Int32 BulkWriteLimit = 1250; // limit of each bulk write to mongodb
193 changes: 138 additions & 55 deletions src/libiec61850/dotnet/core/2.0/iec61850_client/MongoCommands.cs
Original file line number Diff line number Diff line change
@@ -148,6 +148,7 @@ await collection
js_cmd_tag = change.FullDocument.tag.ToString(),
value = change.FullDocument.value.ToDouble(),
fc = change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper(),
useSelectWithValue = change.FullDocument.protocolSourceCommandUseSBO.AsBoolean,
iecEntry = srv.entries[change.FullDocument.protocolSourceObjectAddress.ToString() + change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper()],
};

@@ -246,116 +247,198 @@ await collection

ControlModel controlModel = control.GetControlModel();
MmsType controlType = control.GetCtlValType();
Log(ic.iecEntry.path + " has control model " + controlModel.ToString());
Log(" type of ctlVal: " + controlType.ToString());
Log(srv.name + " " + ic.iecEntry.path + " has control model " + controlModel.ToString());
Log(srv.name + " type of ctlVal: " + controlType.ToString());

switch (controlModel)
{
case ControlModel.STATUS_ONLY:
okres = false;
Log("Control is status-only!");
Log(srv.name + " Control is status-only!");
break;
case ControlModel.DIRECT_NORMAL:
case ControlModel.DIRECT_ENHANCED:
case ControlModel.DIRECT_ENHANCED:
switch (controlType)
{
case MmsType.MMS_BOOLEAN:
if (control.Operate(ic.value != 0))
if (control.Operate(ic.value != 0))
{
okres = true;
Log("Operated successfully!");
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log("Operate failed!");
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
case MmsType.MMS_UNSIGNED:
case MmsType.MMS_INTEGER:
if (control.Operate(ic.value != 0))
{
okres = true;
Log("Operated successfully!");
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log("Operate failed!");
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
case MmsType.MMS_FLOAT:
if (control.Operate(ic.value != 0))
{
okres = true;
Log("Operated successfully!");
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log("Operate failed!");
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
default:
Log("Unsupported Command Type!");
Log(srv.name + " Unsupported Command Type!");
break;
}
break;
case ControlModel.SBO_NORMAL:
case ControlModel.SBO_ENHANCED:
if (control.Select())
switch (controlType)
{
switch (controlType)
{
case MmsType.MMS_BOOLEAN:
if (control.Operate(ic.value != 0))
if (control.Operate(ic.value != 0))
{
okres = true;
Log("Operated successfully!");
}
else
{
okres = false;
Log("Operate failed!");
}
break;
case MmsType.MMS_UNSIGNED:
case MmsType.MMS_INTEGER:
if (control.Operate(ic.value != 0))
case MmsType.MMS_BOOLEAN:
if (ic.useSelectWithValue)
{
Log(srv.name + " Selecting with value...");
if (!control.SelectWithValue(ic.value != 0))
{
okres = true;
Log("Operated successfully!");
okres = false;
Log(srv.name + " Select with value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
else
}
else
{
Log(srv.name + " Selecting without value...");
if (!control.Select())
{
okres = false;
Log("Operate failed!");
Log(srv.name + " Select without value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
break;
case MmsType.MMS_FLOAT:
if (control.Operate(ic.value != 0))
}
Log(srv.name + " Selected successfully!");
Thread.Sleep(100);
if (control.Operate(ic.value != 0))
{
okres = true;
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
case MmsType.MMS_UNSIGNED:
case MmsType.MMS_INTEGER:
if (ic.useSelectWithValue)
{
Log(srv.name + " Selecting with value...");
if (!control.SelectWithValue((int)ic.value))
{
okres = true;
Log("Operated successfully!");
okres = false;
Log(srv.name + " Select with value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
else
}
else
{
Log(srv.name + " Selecting without value...");
if (!control.Select())
{
okres = false;
Log("Operate failed!");
Log(srv.name + " Select without value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
break;
default:
Log("Unsupported Command Type!");
break;
}
}
else
{
okres = false;
Log("Select failed!");
}
Log(srv.name + " Selected successfully!");
Thread.Sleep(100);
if (control.Operate((int)ic.value))
{
okres = true;
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
case MmsType.MMS_FLOAT:
if (ic.useSelectWithValue)
{
Log(srv.name + " Selecting with value...");
if (!control.SelectWithValue((float)ic.value))
{
okres = false;
Log(srv.name + " Select with value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
}
else
{
Log(srv.name + " Selecting without value...");
if (!control.Select())
{
okres = false;
Log(srv.name + " Select without value failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
break;
}
}
Log(srv.name + " Selected successfully!");
Thread.Sleep(100);
if (control.Operate((float)ic.value))
{
okres = true;
Log(srv.name + " Operated successfully!");
}
else
{
okres = false;
Log(srv.name + " Operate failed!");
Log(srv.name + " Error: " + control.GetLastApplError().error);
Log(srv.name + " Addit.Cause: " + control.GetLastApplError().addCause);
}
break;
default:
Log(srv.name + " Unsupported Command Type!");
break;
}

break;
}
control.Dispose();
@@ -413,8 +496,8 @@ await collection
(
!srv.commandsEnabled
? " Commands Disabled"
: srv.entries.ContainsKey(change.FullDocument.protocolSourceObjectAddress.ToString() +
change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper())
: srv.entries.ContainsKey(change.FullDocument.protocolSourceObjectAddress.ToString() +
change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper())
? " Not connected" : " Command not found!"
));
var filter =
@@ -428,8 +511,8 @@ await collection
(
!srv.commandsEnabled
? "commands disabled"
: srv.entries.ContainsKey(change.FullDocument.protocolSourceObjectAddress.ToString() +
change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper())
: srv.entries.ContainsKey(change.FullDocument.protocolSourceObjectAddress.ToString() +
change.FullDocument.protocolSourceCommonAddress.ToString().ToUpper())
? "not connected" : "command not found!"
)));
var result =

3 comments on commit 73028a5

@arifmjcet
Copy link

@arifmjcet arifmjcet commented on 73028a5 Nov 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow! I have tested with IEDs of different vendors. Now SBO enhanced Commands are working . Thanks great work. It will be even better if client driver tries to reconnect with the ieds if ieds get disconnected from the network and get reconnected again.Now only restarting the driver is working.

@riclolsen
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reporting that, Arif. I will look into the connection issue.

@riclolsen
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code updated with auto re-connection.

Please sign in to comment.