hat.drivers.iec104
IEC 60870-5-104 communication protocol
1"""IEC 60870-5-104 communication protocol""" 2 3from hat.drivers.iec104.common import (AsduTypeError, 4 TimeSize, 5 Time, 6 OriginatorAddress, 7 AsduAddress, 8 IoAddress, 9 IndicationQuality, 10 MeasurementQuality, 11 CounterQuality, 12 ProtectionQuality, 13 Quality, 14 FreezeCode, 15 SingleValue, 16 DoubleValue, 17 RegulatingValue, 18 StepPositionValue, 19 BitstringValue, 20 NormalizedValue, 21 ScaledValue, 22 FloatingValue, 23 BinaryCounterValue, 24 ProtectionValue, 25 ProtectionStartValue, 26 ProtectionCommandValue, 27 StatusValue, 28 OtherCause, 29 DataResCause, 30 DataCause, 31 CommandReqCause, 32 CommandResCause, 33 CommandCause, 34 InitializationResCause, 35 InitializationCause, 36 ReadReqCause, 37 ReadResCause, 38 ReadCause, 39 ClockSyncReqCause, 40 ClockSyncResCause, 41 ClockSyncCause, 42 ActivationReqCause, 43 ActivationResCause, 44 ActivationCause, 45 DelayReqCause, 46 DelayResCause, 47 DelayCause, 48 ParameterReqCause, 49 ParameterResCause, 50 ParameterCause, 51 ParameterActivationReqCause, 52 ParameterActivationResCause, 53 ParameterActivationCause, 54 SingleData, 55 DoubleData, 56 StepPositionData, 57 BitstringData, 58 NormalizedData, 59 ScaledData, 60 FloatingData, 61 BinaryCounterData, 62 ProtectionData, 63 ProtectionStartData, 64 ProtectionCommandData, 65 StatusData, 66 Data, 67 SingleCommand, 68 DoubleCommand, 69 RegulatingCommand, 70 NormalizedCommand, 71 ScaledCommand, 72 FloatingCommand, 73 BitstringCommand, 74 Command, 75 NormalizedParameter, 76 ScaledParameter, 77 FloatingParameter, 78 Parameter, 79 DataMsg, 80 CommandMsg, 81 InitializationMsg, 82 InterrogationMsg, 83 CounterInterrogationMsg, 84 ReadMsg, 85 ClockSyncMsg, 86 TestMsg, 87 ResetMsg, 88 ParameterMsg, 89 ParameterActivationMsg, 90 Msg, 91 time_from_datetime, 92 time_to_datetime, 93 Connection, 94 Function) 95from hat.drivers.iec104.connection import (ConnectionCb, 96 connect, 97 listen, 98 Server) 99 100 101__all__ = ['AsduTypeError', 102 'TimeSize', 103 'Time', 104 'OriginatorAddress', 105 'AsduAddress', 106 'IoAddress', 107 'IndicationQuality', 108 'MeasurementQuality', 109 'CounterQuality', 110 'ProtectionQuality', 111 'Quality', 112 'FreezeCode', 113 'SingleValue', 114 'DoubleValue', 115 'RegulatingValue', 116 'StepPositionValue', 117 'BitstringValue', 118 'NormalizedValue', 119 'ScaledValue', 120 'FloatingValue', 121 'BinaryCounterValue', 122 'ProtectionValue', 123 'ProtectionStartValue', 124 'ProtectionCommandValue', 125 'StatusValue', 126 'OtherCause', 127 'DataResCause', 128 'DataCause', 129 'CommandReqCause', 130 'CommandResCause', 131 'CommandCause', 132 'InitializationResCause', 133 'InitializationCause', 134 'ReadReqCause', 135 'ReadResCause', 136 'ReadCause', 137 'ClockSyncReqCause', 138 'ClockSyncResCause', 139 'ClockSyncCause', 140 'ActivationReqCause', 141 'ActivationResCause', 142 'ActivationCause', 143 'DelayReqCause', 144 'DelayResCause', 145 'DelayCause', 146 'ParameterReqCause', 147 'ParameterResCause', 148 'ParameterCause', 149 'ParameterActivationReqCause', 150 'ParameterActivationResCause', 151 'ParameterActivationCause', 152 'SingleData', 153 'DoubleData', 154 'StepPositionData', 155 'BitstringData', 156 'NormalizedData', 157 'ScaledData', 158 'FloatingData', 159 'BinaryCounterData', 160 'ProtectionData', 161 'ProtectionStartData', 162 'ProtectionCommandData', 163 'StatusData', 164 'Data', 165 'SingleCommand', 166 'DoubleCommand', 167 'RegulatingCommand', 168 'NormalizedCommand', 169 'ScaledCommand', 170 'FloatingCommand', 171 'BitstringCommand', 172 'Command', 173 'NormalizedParameter', 174 'ScaledParameter', 175 'FloatingParameter', 176 'Parameter', 177 'DataMsg', 178 'CommandMsg', 179 'InitializationMsg', 180 'InterrogationMsg', 181 'CounterInterrogationMsg', 182 'ReadMsg', 183 'ClockSyncMsg', 184 'TestMsg', 185 'ResetMsg', 186 'ParameterMsg', 187 'ParameterActivationMsg', 188 'Msg', 189 'time_from_datetime', 190 'time_to_datetime', 191 'Connection', 192 'Function', 193 'ConnectionCb', 194 'connect', 195 'listen', 196 'Server']
Common base class for all non-exit exceptions.
An enumeration.
31class Time(typing.NamedTuple): 32 size: TimeSize 33 milliseconds: int 34 """milliseconds in range [0, 59999]""" 35 invalid: bool | None 36 """available for size THREE, FOUR, SEVEN""" 37 minutes: int | None 38 """available for size THREE, FOUR, SEVEN (minutes in range [0, 59])""" 39 summer_time: bool | None 40 """available for size FOUR, SEVEN""" 41 hours: int | None 42 """available for size FOUR, SEVEN (hours in range [0, 23])""" 43 day_of_week: int | None 44 """available for size SEVEN (day_of_week in range [1, 7])""" 45 day_of_month: int | None 46 """available for size SEVEN (day_of_month in range [1, 31])""" 47 months: int | None 48 """available for size SEVEN (months in range [1, 12])""" 49 years: int | None 50 """available for size SEVEN (years in range [0, 99])"""
Time(size, milliseconds, invalid, minutes, summer_time, hours, day_of_week, day_of_month, months, years)
Create new instance of Time(size, milliseconds, invalid, minutes, summer_time, hours, day_of_week, day_of_month, months, years)
147class IndicationQuality(typing.NamedTuple): 148 invalid: bool 149 not_topical: bool 150 substituted: bool 151 blocked: bool
IndicationQuality(invalid, not_topical, substituted, blocked)
154class MeasurementQuality(typing.NamedTuple): 155 invalid: bool 156 not_topical: bool 157 substituted: bool 158 blocked: bool 159 overflow: bool
MeasurementQuality(invalid, not_topical, substituted, blocked, overflow)
162class CounterQuality(typing.NamedTuple): 163 invalid: bool 164 adjusted: bool 165 overflow: bool 166 sequence: int 167 """sequence in range [0, 31]"""
CounterQuality(invalid, adjusted, overflow, sequence)
170class ProtectionQuality(typing.NamedTuple): 171 invalid: bool 172 not_topical: bool 173 substituted: bool 174 blocked: bool 175 time_invalid: bool
ProtectionQuality(invalid, not_topical, substituted, blocked, time_invalid)
An enumeration.
An enumeration.
196class DoubleValue(enum.Enum): 197 """DoubleDataValue 198 199 `FAULT` stands for value 3, defined in the protocol as *INDETERMINATE*. 200 This is in order to make it more distinguishable from ``INTERMEDIATE``. 201 202 """ 203 INTERMEDIATE = 0 204 OFF = 1 205 ON = 2 206 FAULT = 3
DoubleDataValue
FAULT
stands for value 3, defined in the protocol as INDETERMINATE.
This is in order to make it more distinguishable from INTERMEDIATE
.
An enumeration.
214class StepPositionValue(typing.NamedTuple): 215 value: int 216 """value in range [-64, 63]""" 217 transient: bool
StepPositionValue(value, transient)
220class BitstringValue(typing.NamedTuple): 221 value: util.Bytes 222 """bitstring encoded as 4 bytes"""
BitstringValue(value,)
NormalizedValue(value,)
ScaledValue(value,)
FloatingValue(value,)
239class BinaryCounterValue(typing.NamedTuple): 240 value: int 241 """value in range [-2^31, 2^31-1]"""
BinaryCounterValue(value,)
An enumeration.
249class ProtectionStartValue(typing.NamedTuple): 250 general: bool 251 l1: bool 252 l2: bool 253 l3: bool 254 ie: bool 255 reverse: bool
ProtectionStartValue(general, l1, l2, l3, ie, reverse)
258class ProtectionCommandValue(typing.NamedTuple): 259 general: bool 260 l1: bool 261 l2: bool 262 l3: bool
ProtectionCommandValue(general, l1, l2, l3)
265class StatusValue(typing.NamedTuple): 266 value: list[bool] 267 """value length is 16""" 268 change: list[bool] 269 """change length is 16"""
StatusValue(value, change)
50class DataResCause(enum.Enum): 51 PERIODIC = iec101.CauseType.PERIODIC.value 52 BACKGROUND_SCAN = iec101.CauseType.BACKGROUND_SCAN.value 53 SPONTANEOUS = iec101.CauseType.SPONTANEOUS.value 54 REQUEST = iec101.CauseType.REQUEST.value 55 REMOTE_COMMAND = iec101.CauseType.REMOTE_COMMAND.value 56 LOCAL_COMMAND = iec101.CauseType.LOCAL_COMMAND.value 57 INTERROGATED_STATION = iec101.CauseType.INTERROGATED_STATION.value 58 INTERROGATED_GROUP01 = iec101.CauseType.INTERROGATED_GROUP01.value 59 INTERROGATED_GROUP02 = iec101.CauseType.INTERROGATED_GROUP02.value 60 INTERROGATED_GROUP03 = iec101.CauseType.INTERROGATED_GROUP03.value 61 INTERROGATED_GROUP04 = iec101.CauseType.INTERROGATED_GROUP04.value 62 INTERROGATED_GROUP05 = iec101.CauseType.INTERROGATED_GROUP05.value 63 INTERROGATED_GROUP06 = iec101.CauseType.INTERROGATED_GROUP06.value 64 INTERROGATED_GROUP07 = iec101.CauseType.INTERROGATED_GROUP07.value 65 INTERROGATED_GROUP08 = iec101.CauseType.INTERROGATED_GROUP08.value 66 INTERROGATED_GROUP09 = iec101.CauseType.INTERROGATED_GROUP09.value 67 INTERROGATED_GROUP10 = iec101.CauseType.INTERROGATED_GROUP10.value 68 INTERROGATED_GROUP11 = iec101.CauseType.INTERROGATED_GROUP11.value 69 INTERROGATED_GROUP12 = iec101.CauseType.INTERROGATED_GROUP12.value 70 INTERROGATED_GROUP13 = iec101.CauseType.INTERROGATED_GROUP13.value 71 INTERROGATED_GROUP14 = iec101.CauseType.INTERROGATED_GROUP14.value 72 INTERROGATED_GROUP15 = iec101.CauseType.INTERROGATED_GROUP15.value 73 INTERROGATED_GROUP16 = iec101.CauseType.INTERROGATED_GROUP16.value 74 INTERROGATED_COUNTER = iec101.CauseType.INTERROGATED_COUNTER.value 75 INTERROGATED_COUNTER01 = iec101.CauseType.INTERROGATED_COUNTER01.value 76 INTERROGATED_COUNTER02 = iec101.CauseType.INTERROGATED_COUNTER02.value 77 INTERROGATED_COUNTER03 = iec101.CauseType.INTERROGATED_COUNTER03.value 78 INTERROGATED_COUNTER04 = iec101.CauseType.INTERROGATED_COUNTER04.value
An enumeration.
84class CommandReqCause(enum.Enum): 85 ACTIVATION = iec101.CauseType.ACTIVATION.value 86 DEACTIVATION = iec101.CauseType.DEACTIVATION.value
An enumeration.
89class CommandResCause(enum.Enum): 90 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 91 DEACTIVATION_CONFIRMATION = iec101.CauseType.DEACTIVATION_CONFIRMATION.value # NOQA 92 ACTIVATION_TERMINATION = iec101.CauseType.ACTIVATION_TERMINATION.value 93 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 94 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 95 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 96 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
102class InitializationResCause(enum.Enum): 103 LOCAL_POWER = 0 104 LOCAL_RESET = 1 105 REMOTE_RESET = 2
An enumeration.
An enumeration.
115class ReadResCause(enum.Enum): 116 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 117 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 118 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 119 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
An enumeration.
129class ClockSyncResCause(enum.Enum): 130 SPONTANEOUS = iec101.CauseType.SPONTANEOUS.value 131 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 132 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 133 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 134 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 135 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
An enumeration.
147class ActivationResCause(enum.Enum): 148 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 149 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 150 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 151 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 152 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
160class DelayReqCause(enum.Enum): 161 SPONTANEOUS = iec101.CauseType.SPONTANEOUS.value 162 ACTIVATION = iec101.CauseType.ACTIVATION.value
An enumeration.
165class DelayResCause(enum.Enum): 166 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 167 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 168 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 169 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 170 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
176class ParameterReqCause(enum.Enum): 177 SPONTANEOUS = iec101.CauseType.SPONTANEOUS.value 178 ACTIVATION = iec101.CauseType.ACTIVATION.value
An enumeration.
181class ParameterResCause(enum.Enum): 182 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 183 INTERROGATED_STATION = iec101.CauseType.INTERROGATED_STATION.value 184 INTERROGATED_GROUP01 = iec101.CauseType.INTERROGATED_GROUP01.value 185 INTERROGATED_GROUP02 = iec101.CauseType.INTERROGATED_GROUP02.value 186 INTERROGATED_GROUP03 = iec101.CauseType.INTERROGATED_GROUP03.value 187 INTERROGATED_GROUP04 = iec101.CauseType.INTERROGATED_GROUP04.value 188 INTERROGATED_GROUP05 = iec101.CauseType.INTERROGATED_GROUP05.value 189 INTERROGATED_GROUP06 = iec101.CauseType.INTERROGATED_GROUP06.value 190 INTERROGATED_GROUP07 = iec101.CauseType.INTERROGATED_GROUP07.value 191 INTERROGATED_GROUP08 = iec101.CauseType.INTERROGATED_GROUP08.value 192 INTERROGATED_GROUP09 = iec101.CauseType.INTERROGATED_GROUP09.value 193 INTERROGATED_GROUP10 = iec101.CauseType.INTERROGATED_GROUP10.value 194 INTERROGATED_GROUP11 = iec101.CauseType.INTERROGATED_GROUP11.value 195 INTERROGATED_GROUP12 = iec101.CauseType.INTERROGATED_GROUP12.value 196 INTERROGATED_GROUP13 = iec101.CauseType.INTERROGATED_GROUP13.value 197 INTERROGATED_GROUP14 = iec101.CauseType.INTERROGATED_GROUP14.value 198 INTERROGATED_GROUP15 = iec101.CauseType.INTERROGATED_GROUP15.value 199 INTERROGATED_GROUP16 = iec101.CauseType.INTERROGATED_GROUP16.value 200 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 201 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 202 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 203 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
211class ParameterActivationReqCause(enum.Enum): 212 ACTIVATION = iec101.CauseType.ACTIVATION.value 213 DEACTIVATION = iec101.CauseType.DEACTIVATION.value
An enumeration.
216class ParameterActivationResCause(enum.Enum): 217 ACTIVATION_CONFIRMATION = iec101.CauseType.ACTIVATION_CONFIRMATION.value 218 DEACTIVATION_CONFIRMATION = iec101.CauseType.DEACTIVATION_CONFIRMATION.value # NOQA 219 UNKNOWN_TYPE = iec101.CauseType.UNKNOWN_TYPE.value 220 UNKNOWN_CAUSE = iec101.CauseType.UNKNOWN_CAUSE.value 221 UNKNOWN_ASDU_ADDRESS = iec101.CauseType.UNKNOWN_ASDU_ADDRESS.value 222 UNKNOWN_IO_ADDRESS = iec101.CauseType.UNKNOWN_IO_ADDRESS.value
An enumeration.
SingleData(value, quality)
Create new instance of SingleData(value, quality)
DoubleData(value, quality)
Create new instance of DoubleData(value, quality)
240class StepPositionData(typing.NamedTuple): 241 value: StepPositionValue 242 quality: MeasurementQuality
StepPositionData(value, quality)
Create new instance of StepPositionData(value, quality)
245class BitstringData(typing.NamedTuple): 246 value: BitstringValue 247 quality: MeasurementQuality
BitstringData(value, quality)
Create new instance of BitstringData(value, quality)
250class NormalizedData(typing.NamedTuple): 251 value: NormalizedValue 252 quality: MeasurementQuality | None
NormalizedData(value, quality)
Create new instance of NormalizedData(value, quality)
ScaledData(value, quality)
Create new instance of ScaledData(value, quality)
FloatingData(value, quality)
Create new instance of FloatingData(value, quality)
265class BinaryCounterData(typing.NamedTuple): 266 value: BinaryCounterValue 267 quality: CounterQuality
BinaryCounterData(value, quality)
Create new instance of BinaryCounterData(value, quality)
270class ProtectionData(typing.NamedTuple): 271 value: ProtectionValue 272 quality: ProtectionQuality 273 elapsed_time: int 274 """elapsed_time in range [0, 65535]"""
ProtectionData(value, quality, elapsed_time)
Create new instance of ProtectionData(value, quality, elapsed_time)
277class ProtectionStartData(typing.NamedTuple): 278 value: ProtectionStartValue 279 quality: ProtectionQuality 280 duration_time: int 281 """duration_time in range [0, 65535]"""
ProtectionStartData(value, quality, duration_time)
Create new instance of ProtectionStartData(value, quality, duration_time)
284class ProtectionCommandData(typing.NamedTuple): 285 value: ProtectionCommandValue 286 quality: ProtectionQuality 287 operating_time: int 288 """operating_time in range [0, 65535]"""
ProtectionCommandData(value, quality, operating_time)
Create new instance of ProtectionCommandData(value, quality, operating_time)
StatusData(value, quality)
Create new instance of StatusData(value, quality)
310class SingleCommand(typing.NamedTuple): 311 value: SingleValue 312 select: bool 313 qualifier: int 314 """qualifier in range [0, 31]"""
SingleCommand(value, select, qualifier)
Create new instance of SingleCommand(value, select, qualifier)
317class DoubleCommand(typing.NamedTuple): 318 value: DoubleValue 319 select: bool 320 qualifier: int 321 """qualifier in range [0, 31]"""
DoubleCommand(value, select, qualifier)
Create new instance of DoubleCommand(value, select, qualifier)
324class RegulatingCommand(typing.NamedTuple): 325 value: RegulatingValue 326 select: bool 327 qualifier: int 328 """qualifier in range [0, 31]"""
RegulatingCommand(value, select, qualifier)
Create new instance of RegulatingCommand(value, select, qualifier)
NormalizedCommand(value, select)
Create new instance of NormalizedCommand(value, select)
ScaledCommand(value, select)
Create new instance of ScaledCommand(value, select)
FloatingCommand(value, select)
Create new instance of FloatingCommand(value, select)
BitstringCommand(value,)
359class NormalizedParameter(typing.NamedTuple): 360 value: NormalizedValue 361 qualifier: int 362 """qualifier in range [0, 255]"""
NormalizedParameter(value, qualifier)
Create new instance of NormalizedParameter(value, qualifier)
365class ScaledParameter(typing.NamedTuple): 366 value: ScaledValue 367 qualifier: int 368 """qualifier in range [0, 255]"""
ScaledParameter(value, qualifier)
Create new instance of ScaledParameter(value, qualifier)
371class FloatingParameter(typing.NamedTuple): 372 value: FloatingValue 373 qualifier: int 374 """qualifier in range [0, 255]"""
FloatingParameter(value, qualifier)
Create new instance of FloatingParameter(value, qualifier)
382class DataMsg(typing.NamedTuple): 383 is_test: bool 384 originator_address: OriginatorAddress 385 asdu_address: AsduAddress 386 io_address: IoAddress 387 data: Data 388 time: Time | None 389 cause: DataCause
DataMsg(is_test, originator_address, asdu_address, io_address, data, time, cause)
Create new instance of DataMsg(is_test, originator_address, asdu_address, io_address, data, time, cause)
Alias for field number 4
125class CommandMsg(typing.NamedTuple): 126 is_test: bool 127 originator_address: OriginatorAddress 128 asdu_address: AsduAddress 129 io_address: IoAddress 130 command: Command 131 is_negative_confirm: bool 132 time: Time | None 133 cause: CommandCause
CommandMsg(is_test, originator_address, asdu_address, io_address, command, is_negative_confirm, time, cause)
Create new instance of CommandMsg(is_test, originator_address, asdu_address, io_address, command, is_negative_confirm, time, cause)
Alias for field number 4
402class InitializationMsg(typing.NamedTuple): 403 is_test: bool 404 originator_address: OriginatorAddress 405 asdu_address: AsduAddress 406 param_change: bool 407 cause: InitializationCause
InitializationMsg(is_test, originator_address, asdu_address, param_change, cause)
Create new instance of InitializationMsg(is_test, originator_address, asdu_address, param_change, cause)
410class InterrogationMsg(typing.NamedTuple): 411 is_test: bool 412 originator_address: OriginatorAddress 413 asdu_address: AsduAddress 414 request: int 415 """request in range [0, 255]""" 416 is_negative_confirm: bool 417 cause: CommandCause
InterrogationMsg(is_test, originator_address, asdu_address, request, is_negative_confirm, cause)
Create new instance of InterrogationMsg(is_test, originator_address, asdu_address, request, is_negative_confirm, cause)
420class CounterInterrogationMsg(typing.NamedTuple): 421 is_test: bool 422 originator_address: OriginatorAddress 423 asdu_address: AsduAddress 424 request: int 425 """request in range [0, 63]""" 426 freeze: FreezeCode 427 is_negative_confirm: bool 428 cause: CommandCause
CounterInterrogationMsg(is_test, originator_address, asdu_address, request, freeze, is_negative_confirm, cause)
Create new instance of CounterInterrogationMsg(is_test, originator_address, asdu_address, request, freeze, is_negative_confirm, cause)
431class ReadMsg(typing.NamedTuple): 432 is_test: bool 433 originator_address: OriginatorAddress 434 asdu_address: AsduAddress 435 io_address: IoAddress 436 cause: ReadCause
ReadMsg(is_test, originator_address, asdu_address, io_address, cause)
Create new instance of ReadMsg(is_test, originator_address, asdu_address, io_address, cause)
439class ClockSyncMsg(typing.NamedTuple): 440 is_test: bool 441 originator_address: OriginatorAddress 442 asdu_address: AsduAddress 443 time: Time 444 is_negative_confirm: bool 445 cause: ClockSyncCause
ClockSyncMsg(is_test, originator_address, asdu_address, time, is_negative_confirm, cause)
Create new instance of ClockSyncMsg(is_test, originator_address, asdu_address, time, is_negative_confirm, cause)
136class TestMsg(typing.NamedTuple): 137 is_test: bool 138 originator_address: OriginatorAddress 139 asdu_address: AsduAddress 140 counter: int 141 """counter in range [0, 65535]""" 142 time: Time 143 cause: ActivationCause
TestMsg(is_test, originator_address, asdu_address, counter, time, cause)
Create new instance of TestMsg(is_test, originator_address, asdu_address, counter, time, cause)
455class ResetMsg(typing.NamedTuple): 456 is_test: bool 457 originator_address: OriginatorAddress 458 asdu_address: AsduAddress 459 qualifier: int 460 """qualifier in range [0, 255]""" 461 cause: ActivationCause
ResetMsg(is_test, originator_address, asdu_address, qualifier, cause)
Create new instance of ResetMsg(is_test, originator_address, asdu_address, qualifier, cause)
473class ParameterMsg(typing.NamedTuple): 474 is_test: bool 475 originator_address: OriginatorAddress 476 asdu_address: AsduAddress 477 io_address: IoAddress 478 parameter: Parameter 479 cause: ParameterCause
ParameterMsg(is_test, originator_address, asdu_address, io_address, parameter, cause)
Create new instance of ParameterMsg(is_test, originator_address, asdu_address, io_address, parameter, cause)
482class ParameterActivationMsg(typing.NamedTuple): 483 is_test: bool 484 originator_address: OriginatorAddress 485 asdu_address: AsduAddress 486 io_address: IoAddress 487 qualifier: int 488 """qualifier in range [0, 255]""" 489 cause: ParameterActivationCause
ParameterActivationMsg(is_test, originator_address, asdu_address, io_address, qualifier, cause)
Create new instance of ParameterActivationMsg(is_test, originator_address, asdu_address, io_address, qualifier, cause)
66def time_from_datetime(dt: datetime.datetime, 67 invalid: bool = False 68 ) -> Time: 69 """Create Time from datetime.datetime""" 70 # TODO document edge cases (local time, os implementation, ...) 71 # rounding microseconds to the nearest millisecond 72 dt_rounded = ( 73 dt.replace(microsecond=0) + 74 datetime.timedelta(milliseconds=round(dt.microsecond / 1000))) 75 local_time = time.localtime(dt_rounded.timestamp()) 76 77 return Time( 78 size=TimeSize.SEVEN, 79 milliseconds=(local_time.tm_sec * 1000 + 80 dt_rounded.microsecond // 1000), 81 invalid=invalid, 82 minutes=local_time.tm_min, 83 summer_time=bool(local_time.tm_isdst), 84 hours=local_time.tm_hour, 85 day_of_week=local_time.tm_wday + 1, 86 day_of_month=local_time.tm_mday, 87 months=local_time.tm_mon, 88 years=local_time.tm_year % 100)
Create Time from datetime.datetime
91def time_to_datetime(t: Time 92 ) -> datetime.datetime: 93 """Convert Time to datetime.datetime""" 94 # TODO document edge cases (local time, os implementation, ...) 95 # TODO maybe allow diferent time size (use now for time) 96 if t.size != TimeSize.SEVEN: 97 raise ValueError('unsupported time size') 98 99 local_dt = datetime.datetime( 100 year=2000 + t.years if t.years < 70 else 1900 + t.years, 101 month=t.months, 102 day=t.day_of_month, 103 hour=t.hours, 104 minute=t.minutes, 105 second=int(t.milliseconds / 1000), 106 microsecond=(t.milliseconds % 1000) * 1000, 107 fold=not t.summer_time) 108 109 return local_dt.astimezone(tz=datetime.timezone.utc)
Convert Time to datetime.datetime
163class Connection(aio.Resource): 164 165 @property 166 @abc.abstractmethod 167 def conn(self) -> apci.Connection: 168 pass 169 170 @property 171 def async_group(self) -> aio.Group: 172 return self.conn.async_group 173 174 @property 175 def info(self) -> tcp.ConnectionInfo: 176 return self.conn.info 177 178 @property 179 def is_enabled(self) -> bool: 180 return self.conn.is_enabled 181 182 def register_enabled_cb(self, 183 cb: typing.Callable[[bool], None] 184 ) -> util.RegisterCallbackHandle: 185 return self.conn.register_enabled_cb(cb) 186 187 @abc.abstractmethod 188 async def send(self, msgs: list[Msg], wait_ack: bool = False): 189 pass 190 191 @abc.abstractmethod 192 async def drain(self, wait_ack: bool = False): 193 pass 194 195 @abc.abstractmethod 196 async def receive(self) -> list[Msg]: 197 pass
Resource with lifetime control based on Group
.
200class Function(enum.Enum): 201 DATA = 'data' 202 COMMAND = 'command' 203 INITIALIZATION = 'initialization' 204 INTERROGATION = 'interrogation' 205 COUNTER_INTERROGATION = 'counter_interrogation' 206 READ = 'read' 207 CLOCK_SYNC = 'clock_sync' 208 TEST = 'test' 209 RESET = 'reset' 210 PARAMETER = 'parameter' 211 PARAMETER_ACTIVATION = 'parameter_activation'
An enumeration.
17async def connect(addr: tcp.Address, 18 response_timeout: float = 15, 19 supervisory_timeout: float = 10, 20 test_timeout: float = 20, 21 send_window_size: int = 12, 22 receive_window_size: int = 8, 23 update_key: util.Bytes | None = None, 24 critical_functions: set[common.Function] = secure.default_critical_functions, # NOQA 25 **kwargs 26 ) -> common.Connection: 27 apci_conn = await apci.connect(addr=addr, 28 response_timeout=response_timeout, 29 supervisory_timeout=supervisory_timeout, 30 test_timeout=test_timeout, 31 send_window_size=send_window_size, 32 receive_window_size=receive_window_size, 33 **kwargs) 34 35 if update_key is not None: 36 return secure.SecureConnection(apci_conn, True, update_key, 37 critical_functions) 38 39 return regular.RegularConnection(apci_conn)
42async def listen(connection_cb: ConnectionCb, 43 addr: tcp.Address = tcp.Address('0.0.0.0', 2404), 44 response_timeout: float = 15, 45 supervisory_timeout: float = 10, 46 test_timeout: float = 20, 47 send_window_size: int = 12, 48 receive_window_size: int = 8, 49 update_key: util.Bytes | None = None, 50 critical_functions: set[common.Function] = secure.default_critical_functions, # NOQA 51 **kwargs 52 ) -> 'Server': 53 server = Server() 54 server._connection_cb = connection_cb 55 server._update_key = update_key 56 server._critical_functions = critical_functions 57 server._srv = await apci.listen(connection_cb=server._on_connection, 58 addr=addr, 59 response_timeout=response_timeout, 60 supervisory_timeout=supervisory_timeout, 61 test_timeout=test_timeout, 62 send_window_size=send_window_size, 63 receive_window_size=receive_window_size, 64 **kwargs) 65 return server
68class Server(aio.Resource): 69 70 @property 71 def async_group(self): 72 return self._srv.async_group 73 74 async def _on_connection(self, apci_conn): 75 if self._update_key is None: 76 conn = regular.RegularConnection(apci_conn) 77 78 else: 79 conn = secure.SecureConnection(apci_conn, False, self._update_key, 80 self._critical_functions) 81 82 try: 83 await aio.call(self._connection_cb, conn) 84 85 except BaseException: 86 await aio.uncancellable(conn.async_close())
Resource with lifetime control based on Group
.