Skip to content

Commit 8987d53

Browse files
committed
- CS104 server: added support for multiple redundancy groups
1 parent a40ce32 commit 8987d53

File tree

7 files changed

+586
-67
lines changed

7 files changed

+586
-67
lines changed

CHANGELOG

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
Changes to version 2.2.0
2+
------------------------
3+
- CS104 server: support for multiple redundancy groups
4+
- CS104 server: Added new property ("EnqueuMode") to control behavior when event queue is full
5+
- file service: fixed problem with large directories (#3)
6+
- CS104 server: set TCP nodelay for new socket
7+
- CS101 unbalanced master: fixed state machine problem with multiple slaves (some responses don't change state and master keeps locked on the slave)
8+
- CS101 unbalanced master: reset slave specific FCB value after reset link message
9+
- CS 104 server: fixed bug - deactivate old connections in CONNECTION_IS_REDUNDANCY_GROUP mode
10+
- added project files for .NET core 2.0 (only for CS104 because SerialPort is not supported by .NET core)
11+
112
Changes to version 2.1.0
213
------------------------
314
- support for CS 101 master/slave over TCP/IP
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// /*
2+
// * Copyright 2016, 2017 MZ Automation GmbH
3+
// *
4+
// * This file is part of lib60870.NET
5+
// *
6+
// * lib60870.NET is free software: you can redistribute it and/or modify
7+
// * it under the terms of the GNU General Public License as published by
8+
// * the Free Software Foundation, either version 3 of the License, or
9+
// * (at your option) any later version.
10+
// *
11+
// * lib60870.NET is distributed in the hope that it will be useful,
12+
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// * GNU General Public License for more details.
15+
// *
16+
// * You should have received a copy of the GNU General Public License
17+
// * along with lib60870.NET. If not, see <http://www.gnu.org/licenses/>.
18+
// *
19+
// * See COPYING file for the complete license text.
20+
// */
21+
//
22+
//
23+
using System;
24+
25+
using lib60870;
26+
using lib60870.CS101;
27+
using lib60870.CS104;
28+
using System.Net;
29+
using System.Threading;
30+
31+
namespace cs104_redundancy_server
32+
{
33+
class MainClass
34+
{
35+
private static bool connectionRequestHandler(object parameter, IPAddress ipAddress)
36+
{
37+
Console.WriteLine ("New connection request from IP " + ipAddress.ToString ());
38+
39+
// Allow only known IP addresses!
40+
// You can implement your allowed client whitelist here
41+
if (ipAddress.ToString ().Equals ("127.0.0.1"))
42+
return true;
43+
else if (ipAddress.ToString ().Equals ("192.168.178.70"))
44+
return true;
45+
else if (ipAddress.ToString ().Equals ("192.168.2.9"))
46+
return true;
47+
else
48+
return false;
49+
}
50+
51+
private static void connectionEventHandler(object parameter, ClientConnection connection, ClientConnectionEvent conEvent)
52+
{
53+
Console.WriteLine ("Connection {0}:{1} - {2}", connection.RemoteEndpoint.Address.ToString (),
54+
connection.RemoteEndpoint.Port, conEvent.ToString ());
55+
}
56+
57+
private static bool interrogationHandler(object parameter, IMasterConnection connection, ASDU asdu, byte qoi)
58+
{
59+
Console.WriteLine ("Interrogation for group " + qoi);
60+
61+
ApplicationLayerParameters cp = connection.GetApplicationLayerParameters ();
62+
63+
connection.SendACT_CON (asdu, false);
64+
65+
// send information objects
66+
ASDU newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, false);
67+
68+
newAsdu.AddInformationObject (new MeasuredValueScaled (100, -1, new QualityDescriptor ()));
69+
70+
newAsdu.AddInformationObject (new MeasuredValueScaled (101, 23, new QualityDescriptor ()));
71+
72+
newAsdu.AddInformationObject (new MeasuredValueScaled (102, 2300, new QualityDescriptor ()));
73+
74+
connection.SendASDU (newAsdu);
75+
76+
newAsdu = new ASDU (cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 3, 1, false);
77+
78+
newAsdu.AddInformationObject(new MeasuredValueScaledWithCP56Time2a(103, 3456, new QualityDescriptor (), new CP56Time2a(DateTime.Now)));
79+
80+
connection.SendASDU (newAsdu);
81+
82+
newAsdu = new ASDU (cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, false);
83+
84+
newAsdu.AddInformationObject (new SinglePointWithCP56Time2a (104, true, new QualityDescriptor (), new CP56Time2a (DateTime.Now)));
85+
86+
connection.SendASDU (newAsdu);
87+
88+
// send sequence of information objects
89+
newAsdu = new ASDU (cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, true);
90+
91+
newAsdu.AddInformationObject (new SinglePointInformation (200, true, new QualityDescriptor ()));
92+
newAsdu.AddInformationObject (new SinglePointInformation (201, false, new QualityDescriptor ()));
93+
newAsdu.AddInformationObject (new SinglePointInformation (202, true, new QualityDescriptor ()));
94+
newAsdu.AddInformationObject (new SinglePointInformation (203, false, new QualityDescriptor ()));
95+
newAsdu.AddInformationObject (new SinglePointInformation (204, true, new QualityDescriptor ()));
96+
newAsdu.AddInformationObject (new SinglePointInformation (205, false, new QualityDescriptor ()));
97+
newAsdu.AddInformationObject (new SinglePointInformation (206, true, new QualityDescriptor ()));
98+
newAsdu.AddInformationObject (new SinglePointInformation (207, false, new QualityDescriptor ()));
99+
100+
connection.SendASDU (newAsdu);
101+
102+
newAsdu = new ASDU (cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, true);
103+
104+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (300, -1.0f));
105+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (301, -0.5f));
106+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (302, -0.1f));
107+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (303, .0f));
108+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (304, 0.1f));
109+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (305, 0.2f));
110+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (306, 0.5f));
111+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (307, 0.7f));
112+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (308, 0.99f));
113+
newAsdu.AddInformationObject (new MeasuredValueNormalizedWithoutQuality (309, 1f));
114+
115+
connection.SendASDU (newAsdu);
116+
117+
connection.SendACT_TERM (asdu);
118+
119+
return true;
120+
}
121+
122+
private static bool asduHandler(object parameter, IMasterConnection connection, ASDU asdu)
123+
{
124+
125+
if (asdu.TypeId == TypeID.C_SC_NA_1) {
126+
Console.WriteLine ("Single command");
127+
128+
SingleCommand sc = (SingleCommand)asdu.GetElement (0);
129+
130+
Console.WriteLine (sc.ToString ());
131+
}
132+
else if (asdu.TypeId == TypeID.C_CS_NA_1){
133+
134+
135+
ClockSynchronizationCommand qsc = (ClockSynchronizationCommand)asdu.GetElement (0);
136+
137+
Console.WriteLine ("Received clock sync command with time " + qsc.NewTime.ToString());
138+
}
139+
140+
return true;
141+
}
142+
143+
public static void Main (string[] args)
144+
{
145+
bool running = true;
146+
147+
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
148+
e.Cancel = true;
149+
running = false;
150+
};
151+
152+
Server server = new Server ();
153+
//server.DebugOutput = true;
154+
155+
/* Configure a server with three redundancy groups */
156+
157+
server.ServerMode = ServerMode.MULTIPLE_REDUNDANCY_GROUPS;
158+
server.MaxQueueSize = 10;
159+
server.MaxOpenConnections = 6;
160+
161+
RedundancyGroup redGroup1 = new RedundancyGroup("red-group-1");
162+
redGroup1.AddAllowedClient("192.168.2.9");
163+
164+
RedundancyGroup redGroup2 = new RedundancyGroup("red-group-2");
165+
redGroup2.AddAllowedClient("192.168.2.223");
166+
redGroup2.AddAllowedClient("192.168.2.222");
167+
168+
/* add a "catch all" redundancy groups - all other connections are handled by this group */
169+
RedundancyGroup redGroup3 = new RedundancyGroup("catch all");
170+
171+
server.AddRedundancyGroup(redGroup1);
172+
server.AddRedundancyGroup(redGroup2);
173+
server.AddRedundancyGroup(redGroup3);
174+
175+
server.SetConnectionRequestHandler (connectionRequestHandler, null);
176+
177+
server.SetConnectionEventHandler (connectionEventHandler, null);
178+
179+
server.SetInterrogationHandler (interrogationHandler, null);
180+
181+
server.SetASDUHandler (asduHandler, null);
182+
183+
server.Start ();
184+
185+
int waitTime = 1000;
186+
187+
while (running)
188+
{
189+
Thread.Sleep(100);
190+
191+
if (waitTime > 0)
192+
waitTime -= 100;
193+
else {
194+
195+
ASDU newAsdu = new ASDU (server.GetApplicationLayerParameters(), CauseOfTransmission.PERIODIC, false, false, 2, 1, false);
196+
197+
newAsdu.AddInformationObject (new MeasuredValueScaled (110, -1, new QualityDescriptor ()));
198+
199+
server.EnqueueASDU (newAsdu);
200+
201+
waitTime = 1000;
202+
}
203+
}
204+
205+
Console.WriteLine ("Stop server");
206+
server.Stop ();
207+
}
208+
}
209+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// /*
2+
// * Copyright 2016, 2017 MZ Automation GmbH
3+
// *
4+
// * This file is part of lib60870.NET
5+
// *
6+
// * lib60870.NET is free software: you can redistribute it and/or modify
7+
// * it under the terms of the GNU General Public License as published by
8+
// * the Free Software Foundation, either version 3 of the License, or
9+
// * (at your option) any later version.
10+
// *
11+
// * lib60870.NET is distributed in the hope that it will be useful,
12+
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// * GNU General Public License for more details.
15+
// *
16+
// * You should have received a copy of the GNU General Public License
17+
// * along with lib60870.NET. If not, see <http://www.gnu.org/licenses/>.
18+
// *
19+
// * See COPYING file for the complete license text.
20+
// */
21+
//
22+
//
23+
using System.Reflection;
24+
using System.Runtime.CompilerServices;
25+
26+
// Information about this assembly is defined by the following attributes.
27+
// Change them to the values specific to your project.
28+
29+
[assembly: AssemblyTitle("cs104-redundancy-server")]
30+
[assembly: AssemblyDescription("")]
31+
[assembly: AssemblyConfiguration("")]
32+
[assembly: AssemblyCompany("")]
33+
[assembly: AssemblyProduct("")]
34+
[assembly: AssemblyCopyright("mzillgit")]
35+
[assembly: AssemblyTrademark("")]
36+
[assembly: AssemblyCulture("")]
37+
38+
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
39+
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
40+
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
41+
42+
[assembly: AssemblyVersion("1.0.*")]
43+
44+
// The following attributes are used to specify the signing key for the assembly,
45+
// if desired. See the Mono documentation for more information about signing.
46+
47+
//[assembly: AssemblyDelaySign(false)]
48+
//[assembly: AssemblyKeyFile("")]
49+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
6+
<ProjectGuid>{5DB37425-D880-4159-A2C4-D74F472C3D6B}</ProjectGuid>
7+
<OutputType>Exe</OutputType>
8+
<RootNamespace>cs104redundancyserver</RootNamespace>
9+
<AssemblyName>cs104-redundancy-server</AssemblyName>
10+
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11+
</PropertyGroup>
12+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
13+
<DebugSymbols>true</DebugSymbols>
14+
<DebugType>full</DebugType>
15+
<Optimize>false</Optimize>
16+
<OutputPath>bin\Debug</OutputPath>
17+
<DefineConstants>DEBUG;</DefineConstants>
18+
<ErrorReport>prompt</ErrorReport>
19+
<WarningLevel>4</WarningLevel>
20+
<Externalconsole>true</Externalconsole>
21+
<PlatformTarget>x86</PlatformTarget>
22+
</PropertyGroup>
23+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
24+
<DebugType>full</DebugType>
25+
<Optimize>true</Optimize>
26+
<OutputPath>bin\Release</OutputPath>
27+
<ErrorReport>prompt</ErrorReport>
28+
<WarningLevel>4</WarningLevel>
29+
<Externalconsole>true</Externalconsole>
30+
<PlatformTarget>x86</PlatformTarget>
31+
</PropertyGroup>
32+
<ItemGroup>
33+
<Reference Include="System" />
34+
</ItemGroup>
35+
<ItemGroup>
36+
<Compile Include="Program.cs" />
37+
<Compile Include="Properties\AssemblyInfo.cs" />
38+
</ItemGroup>
39+
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
40+
<ItemGroup>
41+
<ProjectReference Include="..\..\lib60870\lib60870.csproj">
42+
<Project>{F604286A-1AFC-4355-8C2C-A998CFCEE979}</Project>
43+
<Name>lib60870</Name>
44+
</ProjectReference>
45+
</ItemGroup>
46+
</Project>

lib60870.NET.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs101-slave-tcp", "examples
3939
EndProject
4040
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs101-master-tcp", "examples\cs101-master-tcp\cs101-master-tcp.csproj", "{74C5485C-8D49-4676-AFD9-0FECC2F325A4}"
4141
EndProject
42+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cs104-redundancy-server", "examples\cs104-redundancy-server\cs104-redundancy-server.csproj", "{5DB37425-D880-4159-A2C4-D74F472C3D6B}"
43+
EndProject
4244
Global
4345
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4446
Debug|x86 = Debug|x86
@@ -69,6 +71,10 @@ Global
6971
{302416B2-D0C2-47D8-8386-882E168CA840}.Debug|x86.Build.0 = Debug|x86
7072
{302416B2-D0C2-47D8-8386-882E168CA840}.Release|x86.ActiveCfg = Release|x86
7173
{302416B2-D0C2-47D8-8386-882E168CA840}.Release|x86.Build.0 = Release|x86
74+
{5DB37425-D880-4159-A2C4-D74F472C3D6B}.Debug|x86.ActiveCfg = Debug|x86
75+
{5DB37425-D880-4159-A2C4-D74F472C3D6B}.Debug|x86.Build.0 = Debug|x86
76+
{5DB37425-D880-4159-A2C4-D74F472C3D6B}.Release|x86.ActiveCfg = Release|x86
77+
{5DB37425-D880-4159-A2C4-D74F472C3D6B}.Release|x86.Build.0 = Release|x86
7278
{608CD6D9-91FC-419F-A0F6-2091E0B6D469}.Debug|x86.ActiveCfg = Debug|x86
7379
{608CD6D9-91FC-419F-A0F6-2091E0B6D469}.Debug|x86.Build.0 = Debug|x86
7480
{608CD6D9-91FC-419F-A0F6-2091E0B6D469}.Release|x86.ActiveCfg = Release|x86

lib60870/CS104/ClientConnection.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ private struct SentASDU
104104
private int newestSentASDU = -1;
105105
private SentASDU[] sentASDUs = null;
106106

107-
// only available if the server has multiple redundancy groups
108107
private ASDUQueue asduQueue = null;
109108

110109
private FileServer fileServer;
@@ -732,7 +731,7 @@ private bool CheckSequenceNumber(int seqNo) {
732731

733732
/* remove from server (low-priority) queue if required */
734733
if (sentASDUs [oldestSentASDU].queueIndex != -1) {
735-
server.MarkASDUAsConfirmed (sentASDUs [oldestSentASDU].queueIndex,
734+
asduQueue.MarkASDUAsConfirmed (sentASDUs [oldestSentASDU].queueIndex,
736735
sentASDUs [oldestSentASDU].entryTime);
737736
}
738737

@@ -1116,9 +1115,9 @@ private void HandleConnection()
11161115
DebugLog( e.ToString());
11171116
}
11181117

1119-
// unmark unconfirmed messages in server queue if k-buffer not empty
1120-
if (oldestSentASDU != -1)
1121-
server.UnmarkAllASDUs ();
1118+
// unmark unconfirmed messages in queue if k-buffer not empty
1119+
if (oldestSentASDU != -1)
1120+
asduQueue.UnmarkAllASDUs();
11221121

11231122
server.Remove (this);
11241123

0 commit comments

Comments
 (0)