11import logging
22import sys
33
4- from pydnp3 import opendnp3 , openpal , asiopal , asiodnp3
4+ from pydnp3 import asiodnp3 , asiopal , opendnp3 , openpal
55
66LOG_LEVELS = opendnp3 .levels .NORMAL | opendnp3 .levels .ALL_COMMS
77LOCAL_IP = "0.0.0.0"
88PORT = 20000
99
1010stdout_stream = logging .StreamHandler (sys .stdout )
11- stdout_stream .setFormatter (logging .Formatter ('%(asctime)s\t %(name)s\t %(levelname)s\t %(message)s' ))
11+ stdout_stream .setFormatter (
12+ logging .Formatter ("%(asctime)s\t %(name)s\t %(levelname)s\t %(message)s" )
13+ )
1214
1315_log = logging .getLogger (__name__ )
1416_log .addHandler (stdout_stream )
1719
1820class OutstationApplication (opendnp3 .IOutstationApplication ):
1921 """
20- Interface for all outstation callback info except for control requests.
21-
22- DNP3 spec section 5.1.6.2:
23- The Application Layer provides the following services for the DNP3 User Layer in an outstation:
24- - Notifies the DNP3 User Layer when action requests, such as control output,
25- analog output, freeze and file operations, arrive from a master.
26- - Requests data and information from the outstation that is wanted by a master
27- and formats the responses returned to a master.
28- - Assures that event data is successfully conveyed to a master (using
29- Application Layer confirmation).
30- - Sends notifications to the master when the outstation restarts, has queued events,
31- and requires time synchronization.
32-
33- DNP3 spec section 5.1.6.3:
34- The Application Layer requires specific services from the layers beneath it.
35- - Partitioning of fragments into smaller portions for transport reliability.
36- - Knowledge of which device(s) were the source of received messages.
37- - Transmission of messages to specific devices or to all devices.
38- - Message integrity (i.e., error-free reception and transmission of messages).
39- - Knowledge of the time when messages arrive.
40- - Either precise times of transmission or the ability to set time values
41- into outgoing messages.
22+ Interface for all outstation callback info except for control requests.
23+
24+ DNP3 spec section 5.1.6.2:
25+ The Application Layer provides the following services for the DNP3 User Layer in an outstation:
26+ - Notifies the DNP3 User Layer when action requests, such as control output,
27+ analog output, freeze and file operations, arrive from a master.
28+ - Requests data and information from the outstation that is wanted by a master
29+ and formats the responses returned to a master.
30+ - Assures that event data is successfully conveyed to a master (using
31+ Application Layer confirmation).
32+ - Sends notifications to the master when the outstation restarts, has queued events,
33+ and requires time synchronization.
34+
35+ DNP3 spec section 5.1.6.3:
36+ The Application Layer requires specific services from the layers beneath it.
37+ - Partitioning of fragments into smaller portions for transport reliability.
38+ - Knowledge of which device(s) were the source of received messages.
39+ - Transmission of messages to specific devices or to all devices.
40+ - Message integrity (i.e., error-free reception and transmission of messages).
41+ - Knowledge of the time when messages arrive.
42+ - Either precise times of transmission or the ability to set time values
43+ into outgoing messages.
4244 """
4345
4446 outstation = None
4547
4648 def __init__ (self ):
4749 super (OutstationApplication , self ).__init__ ()
4850
49- _log .debug (' Configuring the DNP3 stack.' )
51+ _log .debug (" Configuring the DNP3 stack." )
5052 self .stack_config = self .configure_stack ()
5153
52- _log .debug (' Configuring the outstation database.' )
54+ _log .debug (" Configuring the outstation database." )
5355 self .configure_database (self .stack_config .dbConfig )
5456
55- _log .debug (' Creating a DNP3Manager.' )
57+ _log .debug (" Creating a DNP3Manager." )
5658 threads_to_allocate = 1
5759 # self.log_handler = MyLogger()
58- self .log_handler = asiodnp3 .ConsoleLogger ().Create () # (or use this during regression testing)
60+ self .log_handler = (
61+ asiodnp3 .ConsoleLogger ().Create ()
62+ ) # (or use this during regression testing)
5963 self .manager = asiodnp3 .DNP3Manager (threads_to_allocate , self .log_handler )
6064
61- _log .debug (' Creating the DNP3 channel, a TCP server.' )
65+ _log .debug (" Creating the DNP3 channel, a TCP server." )
6266 self .retry_parameters = asiopal .ChannelRetry ().Default ()
6367 self .listener = AppChannelListener ()
6468 # self.listener = asiodnp3.PrintingChannelListener().Create() # (or use this during regression testing)
65- self .channel = self .manager .AddTCPServer ("server" ,
66- LOG_LEVELS ,
67- self .retry_parameters ,
68- LOCAL_IP ,
69- PORT ,
70- self .listener )
71-
72- _log .debug ('Adding the outstation to the channel.' )
69+ self .channel = self .manager .AddTCPServer (
70+ "server" , LOG_LEVELS , self .retry_parameters , LOCAL_IP , PORT , self .listener
71+ )
72+
73+ _log .debug ("Adding the outstation to the channel." )
7374 self .command_handler = OutstationCommandHandler ()
7475 # self.command_handler = opendnp3.SuccessCommandHandler().Create() # (or use this during regression testing)
75- self .outstation = self .channel .AddOutstation ("outstation" , self .command_handler , self , self .stack_config )
76+ self .outstation = self .channel .AddOutstation (
77+ "outstation" , self .command_handler , self , self .stack_config
78+ )
7679
7780 # Put the Outstation singleton in OutstationApplication so that it can be used to send updates to the Master.
7881 OutstationApplication .set_outstation (self .outstation )
7982
80- _log .debug (' Enabling the outstation. Traffic will now start to flow.' )
83+ _log .debug (" Enabling the outstation. Traffic will now start to flow." )
8184 self .outstation .Enable ()
8285
8386 @staticmethod
8487 def configure_stack ():
8588 """Set up the OpenDNP3 configuration."""
86- stack_config = asiodnp3 .OutstationStackConfig (opendnp3 .DatabaseSizes .AllTypes (10 ))
87- stack_config .outstation .eventBufferConfig = opendnp3 .EventBufferConfig ().AllTypes (10 )
89+ stack_config = asiodnp3 .OutstationStackConfig (
90+ opendnp3 .DatabaseSizes .AllTypes (10 )
91+ )
92+ stack_config .outstation .eventBufferConfig = (
93+ opendnp3 .EventBufferConfig ().AllTypes (10 )
94+ )
8895 stack_config .outstation .params .allowUnsolicited = True
8996 stack_config .link .LocalAddr = 10
9097 stack_config .link .RemoteAddr = 1
@@ -94,10 +101,10 @@ def configure_stack():
94101 @staticmethod
95102 def configure_database (db_config ):
96103 """
97- Configure the Outstation's database of input point definitions.
104+ Configure the Outstation's database of input point definitions.
98105
99- Configure two Analog points (group/variation 30.1) at indexes 1 and 2.
100- Configure two Binary points (group/variation 1.2) at indexes 1 and 2.
106+ Configure two Analog points (group/variation 30.1) at indexes 1 and 2.
107+ Configure two Binary points (group/variation 1.2) at indexes 1 and 2.
101108 """
102109 db_config .analog [1 ].clazz = opendnp3 .PointClass .Class2
103110 db_config .analog [1 ].svariation = opendnp3 .StaticAnalogVariation .Group30Var1
@@ -114,9 +121,9 @@ def configure_database(db_config):
114121
115122 def shutdown (self ):
116123 """
117- Execute an orderly shutdown of the Outstation.
124+ Execute an orderly shutdown of the Outstation.
118125
119- The debug messages may be helpful if errors occur during shutdown.
126+ The debug messages may be helpful if errors occur during shutdown.
120127 """
121128 # _log.debug('Exiting application...')
122129 # _log.debug('Shutting down outstation...')
@@ -138,17 +145,17 @@ def get_outstation(cls):
138145 @classmethod
139146 def set_outstation (cls , outstn ):
140147 """
141- Set the singleton instance of IOutstation, as returned from the channel's AddOutstation call.
148+ Set the singleton instance of IOutstation, as returned from the channel's AddOutstation call.
142149
143- Making IOutstation available as a singleton allows other classes (e.g. the command-line UI)
144- to send commands to it -- see apply_update().
150+ Making IOutstation available as a singleton allows other classes (e.g. the command-line UI)
151+ to send commands to it -- see apply_update().
145152 """
146153 cls .outstation = outstn
147154
148155 # Overridden method
149156 def ColdRestartSupport (self ):
150157 """Return a RestartMode enumerated value indicating whether cold restart is supported."""
151- _log .debug (' In OutstationApplication.ColdRestartSupport' )
158+ _log .debug (" In OutstationApplication.ColdRestartSupport" )
152159 return opendnp3 .RestartMode .UNSUPPORTED
153160
154161 # Overridden method
@@ -161,29 +168,32 @@ def GetApplicationIIN(self):
161168 application_iin .needTime = False
162169 # Just for testing purposes, convert it to an IINField and display the contents of the two bytes.
163170 iin_field = application_iin .ToIIN ()
164- _log .debug ('OutstationApplication.GetApplicationIIN: IINField LSB={}, MSB={}' .format (iin_field .LSB ,
165- iin_field .MSB ))
171+ _log .debug (
172+ "OutstationApplication.GetApplicationIIN: IINField LSB={}, MSB={}" .format (
173+ iin_field .LSB , iin_field .MSB
174+ )
175+ )
166176 return application_iin
167177
168178 # Overridden method
169179 def SupportsAssignClass (self ):
170- _log .debug (' In OutstationApplication.SupportsAssignClass' )
180+ _log .debug (" In OutstationApplication.SupportsAssignClass" )
171181 return False
172182
173183 # Overridden method
174184 def SupportsWriteAbsoluteTime (self ):
175- _log .debug (' In OutstationApplication.SupportsWriteAbsoluteTime' )
185+ _log .debug (" In OutstationApplication.SupportsWriteAbsoluteTime" )
176186 return False
177187
178188 # Overridden method
179189 def SupportsWriteTimeAndInterval (self ):
180- _log .debug (' In OutstationApplication.SupportsWriteTimeAndInterval' )
190+ _log .debug (" In OutstationApplication.SupportsWriteTimeAndInterval" )
181191 return False
182192
183193 # Overridden method
184194 def WarmRestartSupport (self ):
185195 """Return a RestartMode enumerated value indicating whether a warm restart is supported."""
186- _log .debug (' In OutstationApplication.WarmRestartSupport' )
196+ _log .debug (" In OutstationApplication.WarmRestartSupport" )
187197 return opendnp3 .RestartMode .UNSUPPORTED
188198
189199 @classmethod
@@ -196,7 +206,9 @@ def process_point_value(cls, command_type, command, index, op_type):
196206 :param index: (integer) DNP3 index of the payload's data definition.
197207 :param op_type: An OperateType, or None if command_type == 'Select'.
198208 """
199- _log .debug ('Processing received point value for index {}: {}' .format (index , command ))
209+ _log .debug (
210+ "Processing received point value for index {}: {}" .format (index , command )
211+ )
200212
201213 def apply_update (self , value , index ):
202214 """
@@ -207,7 +219,11 @@ def apply_update(self, value, index):
207219 :param value: An instance of Analog, Binary, or another opendnp3 data value.
208220 :param index: (integer) Index of the data definition in the opendnp3 database.
209221 """
210- _log .debug ('Recording {} measurement, index={}, value={}' .format (type (value ).__name__ , index , value .value ))
222+ _log .debug (
223+ "Recording {} measurement, index={}, value={}" .format (
224+ type (value ).__name__ , index , value .value
225+ )
226+ )
211227 builder = asiodnp3 .UpdateBuilder ()
212228 builder .Update (value , index )
213229 update = builder .Build ()
@@ -216,17 +232,17 @@ def apply_update(self, value, index):
216232
217233class OutstationCommandHandler (opendnp3 .ICommandHandler ):
218234 """
219- Override ICommandHandler in this manner to implement application-specific command handling.
235+ Override ICommandHandler in this manner to implement application-specific command handling.
220236
221- ICommandHandler implements the Outstation's handling of Select and Operate,
222- which relay commands and data from the Master to the Outstation.
237+ ICommandHandler implements the Outstation's handling of Select and Operate,
238+ which relay commands and data from the Master to the Outstation.
223239 """
224240
225241 def Start (self ):
226- _log .debug (' In OutstationCommandHandler.Start' )
242+ _log .debug (" In OutstationCommandHandler.Start" )
227243
228244 def End (self ):
229- _log .debug (' In OutstationCommandHandler.End' )
245+ _log .debug (" In OutstationCommandHandler.End" )
230246
231247 def Select (self , command , index ):
232248 """
@@ -237,7 +253,7 @@ def Select(self, command, index):
237253 :param index: int
238254 :return: CommandStatus
239255 """
240- OutstationApplication .process_point_value (' Select' , command , index , None )
256+ OutstationApplication .process_point_value (" Select" , command , index , None )
241257 return opendnp3 .CommandStatus .SUCCESS
242258
243259 def Operate (self , command , index , op_type ):
@@ -250,46 +266,48 @@ def Operate(self, command, index, op_type):
250266 :param op_type: OperateType
251267 :return: CommandStatus
252268 """
253- OutstationApplication .process_point_value (' Operate' , command , index , op_type )
269+ OutstationApplication .process_point_value (" Operate" , command , index , op_type )
254270 return opendnp3 .CommandStatus .SUCCESS
255271
256272
257273class AppChannelListener (asiodnp3 .IChannelListener ):
258274 """
259- Override IChannelListener in this manner to implement application-specific channel behavior.
275+ Override IChannelListener in this manner to implement application-specific channel behavior.
260276 """
261277
262278 def __init__ (self ):
263279 super (AppChannelListener , self ).__init__ ()
264280
265281 def OnStateChange (self , state ):
266- _log .debug (' In AppChannelListener.OnStateChange: state={}' .format (state ))
282+ _log .debug (" In AppChannelListener.OnStateChange: state={}" .format (state ))
267283
268284
269285class MyLogger (openpal .ILogHandler ):
270286 """
271- Override ILogHandler in this manner to implement application-specific logging behavior.
287+ Override ILogHandler in this manner to implement application-specific logging behavior.
272288 """
273289
274290 def __init__ (self ):
275291 super (MyLogger , self ).__init__ ()
276292
277293 def Log (self , entry ):
278294 filters = entry .filters .GetBitfield ()
279- location = entry .location .rsplit ('/' )[- 1 ] if entry .location else ''
295+ location = entry .location .rsplit ("/" )[- 1 ] if entry .location else ""
280296 message = entry .message
281- _log .debug ('Log\t filters={}\t location={}\t entry={}' .format (filters , location , message ))
297+ _log .debug (
298+ "Log\t filters={}\t location={}\t entry={}" .format (filters , location , message )
299+ )
282300
283301
284302def main ():
285303 """The Outstation has been started from the command line. Execute ad-hoc tests if desired."""
286304 app = OutstationApplication ()
287- _log .debug (' Initialization complete. In command loop.' )
305+ _log .debug (" Initialization complete. In command loop." )
288306 # Ad-hoc tests can be inserted here if desired. See outstation_cmd.py for examples.
289307 app .shutdown ()
290- _log .debug (' Exiting.' )
308+ _log .debug (" Exiting." )
291309 exit ()
292310
293311
294- if __name__ == ' __main__' :
312+ if __name__ == " __main__" :
295313 main ()
0 commit comments