99import logging
1010import struct
1111import time
12- from typing import List , Any , Optional , Tuple , Type , Union
12+ from typing import List , Any , Optional , Tuple , Type
1313from types import TracebackType
1414from datetime import datetime
1515
1616from .connection import TPDUSize
1717from .s7protocol import S7Protocol , get_return_code_description
18- from .datatypes import S7Area , S7WordLen
18+ from .datatypes import S7WordLen
1919from .error import S7Error , S7ConnectionError , S7ProtocolError , S7TimeoutError
20+ from .client_base import ClientMixin
21+ from .type import (
22+ Area ,
23+ Block ,
24+ BlocksList ,
25+ S7CpuInfo ,
26+ TS7BlockInfo ,
27+ S7CpInfo ,
28+ S7OrderCode ,
29+ S7Protection ,
30+ S7SZL ,
31+ Parameter ,
32+ )
2033
2134
2235logger = logging .getLogger (__name__ )
@@ -250,22 +263,8 @@ async def __aexit__(
250263 ) -> None :
251264 await self .disconnect ()
252265
253- from .type import (
254- Area ,
255- Block ,
256- BlocksList ,
257- S7CpuInfo ,
258- TS7BlockInfo ,
259- S7CpInfo ,
260- S7OrderCode ,
261- S7Protection ,
262- S7SZL ,
263- WordLen ,
264- Parameter ,
265- )
266-
267266
268- class AsyncClient :
267+ class AsyncClient ( ClientMixin ) :
269268 """
270269 Native async S7 client implementation.
271270
@@ -1214,102 +1213,6 @@ async def ct_write(self, start: int, size: int, data: bytearray) -> int:
12141213 raise ValueError (f"Data length { len (data )} doesn't match size { size * 2 } " )
12151214 return await self .write_area (Area .CT , 0 , start , data )
12161215
1217- # ---------------------------------------------------------------
1218- # Synchronous helpers (no I/O)
1219- # ---------------------------------------------------------------
1220-
1221- def get_pdu_length (self ) -> int :
1222- """Get negotiated PDU length."""
1223- return self .pdu_length
1224-
1225- def get_exec_time (self ) -> int :
1226- """Get last operation execution time in milliseconds."""
1227- return self ._exec_time
1228-
1229- def get_last_error (self ) -> int :
1230- """Get last error code."""
1231- return self ._last_error
1232-
1233- def error_text (self , error_code : int ) -> str :
1234- """Get error text for error code."""
1235- error_texts = {
1236- 0 : "OK" ,
1237- 0x0001 : "Invalid resource" ,
1238- 0x0002 : "Invalid handle" ,
1239- 0x0003 : "Not connected" ,
1240- 0x0004 : "Connection error" ,
1241- 0x0005 : "Data error" ,
1242- 0x0006 : "Timeout" ,
1243- 0x0007 : "Function not supported" ,
1244- 0x0008 : "Invalid PDU size" ,
1245- 0x0009 : "Invalid PLC answer" ,
1246- 0x000A : "Invalid CPU state" ,
1247- 0x01E00000 : "CPU : Invalid password" ,
1248- 0x00D00000 : "CPU : Invalid value supplied" ,
1249- 0x02600000 : "CLI : Cannot change this param now" ,
1250- }
1251- return error_texts .get (error_code , f"Unknown error: { error_code } " )
1252-
1253- def get_pg_block_info (self , data : bytearray ) -> TS7BlockInfo :
1254- """Get block info from raw block data."""
1255- block_info = TS7BlockInfo ()
1256-
1257- if len (data ) >= 36 :
1258- block_info .BlkType = data [5 ]
1259- block_info .BlkNumber = struct .unpack (">H" , data [6 :8 ])[0 ]
1260- block_info .BlkLang = data [4 ]
1261- block_info .MC7Size = struct .unpack (">I" , data [8 :12 ])[0 ]
1262- block_info .LoadSize = struct .unpack (">I" , data [12 :16 ])[0 ]
1263- block_info .SBBLength = struct .unpack (">I" , data [28 :32 ])[0 ]
1264- block_info .CheckSum = struct .unpack (">H" , data [32 :34 ])[0 ]
1265- block_info .Version = data [34 ]
1266- block_info .CodeDate = b"2019/06/27"
1267- block_info .IntfDate = b"2019/06/27"
1268-
1269- return block_info
1270-
1271- def set_session_password (self , password : str ) -> int :
1272- """Set session password."""
1273- self .session_password = password
1274- return 0
1275-
1276- def clear_session_password (self ) -> int :
1277- """Clear session password."""
1278- self .session_password = None
1279- return 0
1280-
1281- def set_connection_params (self , address : str , local_tsap : int , remote_tsap : int ) -> None :
1282- """Set connection parameters."""
1283- self .host = address
1284- self .local_tsap = local_tsap
1285- self .remote_tsap = remote_tsap
1286-
1287- def set_connection_type (self , connection_type : int ) -> None :
1288- """Set connection type (1=PG, 2=OP, 3=S7Basic)."""
1289- self .connection_type = connection_type
1290-
1291- def get_param (self , param : Parameter ) -> int :
1292- """Get client parameter."""
1293- non_client = [
1294- Parameter .LocalPort , Parameter .WorkInterval , Parameter .MaxClients ,
1295- Parameter .BSendTimeout , Parameter .BRecvTimeout ,
1296- Parameter .RecoveryTime , Parameter .KeepAliveTime ,
1297- ]
1298- if param in non_client :
1299- raise RuntimeError (f"Parameter { param } not valid for client" )
1300- if param == Parameter .SrcTSap :
1301- return self .local_tsap
1302- return self ._params .get (param , 0 )
1303-
1304- def set_param (self , param : Parameter , value : int ) -> int :
1305- """Set client parameter."""
1306- if param == Parameter .RemotePort and self .connected :
1307- raise RuntimeError ("Cannot change RemotePort while connected" )
1308- if param == Parameter .PDURequest :
1309- self .pdu_length = value
1310- self ._params [param ] = value
1311- return 0
1312-
13131216 # ---------------------------------------------------------------
13141217 # Internal helpers
13151218 # ---------------------------------------------------------------
@@ -1328,24 +1231,6 @@ async def _setup_communication(self) -> None:
13281231 self ._params [Parameter .PDURequest ] = self .pdu_length
13291232 logger .info (f"Negotiated PDU length: { self .pdu_length } " )
13301233
1331- def _max_read_size (self ) -> int :
1332- """Maximum payload bytes for a single read request."""
1333- return self .pdu_length - 18
1334-
1335- def _max_write_size (self ) -> int :
1336- """Maximum payload bytes for a single write request."""
1337- return self .pdu_length - 35
1338-
1339- def _map_area (self , area : Area ) -> S7Area :
1340- """Map library area enum to native S7 area."""
1341- area_mapping = {
1342- Area .PE : S7Area .PE , Area .PA : S7Area .PA , Area .MK : S7Area .MK ,
1343- Area .DB : S7Area .DB , Area .CT : S7Area .CT , Area .TM : S7Area .TM ,
1344- }
1345- if area not in area_mapping :
1346- raise S7ProtocolError (f"Unsupported area: { area } " )
1347- return area_mapping [area ]
1348-
13491234 # ---------------------------------------------------------------
13501235 # Context manager
13511236 # ---------------------------------------------------------------
0 commit comments