smartrack-sdk 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of smartrack-sdk might be problematic. Click here for more details.

@@ -0,0 +1,523 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ SmartRack SDK 客户端核心类
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import requests
11
+ from typing import Optional, Dict, Any, Union
12
+ from urllib.parse import urlencode
13
+
14
+ from ..exceptions import (
15
+ SmartRackException,
16
+ SmartRackApiException,
17
+ SmartRackNetworkException,
18
+ SmartRackConfigurationException,
19
+ SmartRackValidationException,
20
+ )
21
+ from ..models.requests import (
22
+ BaseRequest,
23
+ ClearErrorRequest,
24
+ RackTestRequest,
25
+ StockInRequest,
26
+ StockOutRequest,
27
+ StockOutSheetRequest,
28
+ StockOutSheetCancelRequest,
29
+ StockOutSheetFinishRequest,
30
+ StockInFinishRequest,
31
+ ResetCellRequest,
32
+ GetStockByGRNListRequest,
33
+ )
34
+ from ..models.responses import (
35
+ BaseResponse,
36
+ ClearErrorResponse,
37
+ StockDataResponse,
38
+ StockOutResponse,
39
+ StockOutSheetResponse,
40
+ )
41
+ from ..models.enums import LedColors, CellStatus
42
+
43
+ # 配置日志
44
+ logger = logging.getLogger(__name__)
45
+
46
+
47
+ class SmartRackClient:
48
+ """SmartRack SDK 客户端"""
49
+
50
+ def __init__(
51
+ self,
52
+ base_url: str,
53
+ timeout: int = 30,
54
+ auth_token: Optional[str] = None,
55
+ verify_ssl: bool = True,
56
+ ):
57
+ """
58
+ 初始化SmartRack客户端
59
+
60
+ Args:
61
+ base_url: API基础URL
62
+ timeout: 请求超时时间(秒)
63
+ auth_token: 认证Token
64
+ verify_ssl: 是否验证SSL证书
65
+ """
66
+ if not base_url or not base_url.strip():
67
+ raise SmartRackConfigurationException("Base URL cannot be empty")
68
+
69
+ self.base_url = base_url.rstrip('/')
70
+ self.timeout = timeout
71
+ self.auth_token = auth_token
72
+ self.verify_ssl = verify_ssl
73
+
74
+ # 创建HTTP会话
75
+ self.session = requests.Session()
76
+ self.session.timeout = timeout
77
+
78
+ # 设置默认请求头
79
+ self.session.headers.update({
80
+ 'Content-Type': 'application/json',
81
+ 'Accept': 'application/json',
82
+ 'User-Agent': 'SmartRack-Python-SDK/1.0.0',
83
+ })
84
+
85
+ # 设置认证Token
86
+ if auth_token:
87
+ self.set_auth_token(auth_token)
88
+
89
+ logger.info(f"SmartRack客户端初始化完成: {self.base_url}")
90
+
91
+ def set_auth_token(self, token: str) -> None:
92
+ """
93
+ 设置认证Token
94
+
95
+ Args:
96
+ token: 认证Token
97
+ """
98
+ self.auth_token = token
99
+ self.session.headers.update({
100
+ 'Authorization': f'Bearer {token}'
101
+ })
102
+ logger.debug("设置认证Token成功")
103
+
104
+ def clear_auth_token(self) -> None:
105
+ """清除认证Token"""
106
+ self.auth_token = None
107
+ if 'Authorization' in self.session.headers:
108
+ del self.session.headers['Authorization']
109
+ logger.debug("清除认证Token成功")
110
+
111
+ def _make_request(
112
+ self,
113
+ method: str,
114
+ endpoint: str,
115
+ data: Optional[Union[Dict[str, Any], BaseRequest]] = None,
116
+ params: Optional[Dict[str, Any]] = None,
117
+ ) -> Dict[str, Any]:
118
+ """
119
+ 发送HTTP请求
120
+
121
+ Args:
122
+ method: HTTP方法
123
+ endpoint: API端点
124
+ data: 请求数据
125
+ params: 查询参数
126
+
127
+ Returns:
128
+ 响应数据
129
+
130
+ Raises:
131
+ SmartRackNetworkException: 网络错误
132
+ SmartRackApiException: API错误
133
+ """
134
+ url = f"{self.base_url}{endpoint}"
135
+
136
+ # 准备请求参数
137
+ request_kwargs = {
138
+ 'verify': self.verify_ssl,
139
+ 'timeout': self.timeout,
140
+ }
141
+
142
+ # 处理查询参数
143
+ if params:
144
+ request_kwargs['params'] = params
145
+
146
+ # 处理请求数据
147
+ if data is not None:
148
+ if isinstance(data, BaseRequest):
149
+ request_kwargs['json'] = data.to_dict()
150
+ else:
151
+ request_kwargs['json'] = data
152
+
153
+ try:
154
+ logger.debug(f"发送{method}请求到: {url}")
155
+ if request_kwargs.get('json'):
156
+ logger.debug(f"请求数据: {request_kwargs['json']}")
157
+
158
+ response = self.session.request(method, url, **request_kwargs)
159
+
160
+ # 检查HTTP状态码
161
+ response.raise_for_status()
162
+
163
+ # 解析响应
164
+ response_data = response.json()
165
+ logger.debug(f"响应数据: {response_data}")
166
+
167
+ return response_data
168
+
169
+ except requests.exceptions.Timeout as e:
170
+ error_msg = f"请求超时: {str(e)}"
171
+ logger.error(error_msg)
172
+ raise SmartRackNetworkException(error_msg, cause=e)
173
+
174
+ except requests.exceptions.ConnectionError as e:
175
+ error_msg = f"连接错误: {str(e)}"
176
+ logger.error(error_msg)
177
+ raise SmartRackNetworkException(error_msg, cause=e)
178
+
179
+ except requests.exceptions.HTTPError as e:
180
+ error_msg = f"HTTP错误: {str(e)}"
181
+ logger.error(error_msg)
182
+
183
+ # 尝试解析错误响应
184
+ try:
185
+ error_data = e.response.json()
186
+ status_code = e.response.status_code
187
+ raise SmartRackNetworkException(
188
+ f"HTTP {status_code}: {error_data.get('message', error_msg)}",
189
+ status_code=status_code,
190
+ cause=e
191
+ )
192
+ except (ValueError, AttributeError):
193
+ raise SmartRackNetworkException(error_msg, e.response.status_code, e)
194
+
195
+ except requests.exceptions.RequestException as e:
196
+ error_msg = f"请求异常: {str(e)}"
197
+ logger.error(error_msg)
198
+ raise SmartRackNetworkException(error_msg, cause=e)
199
+
200
+ except json.JSONDecodeError as e:
201
+ error_msg = f"JSON解析错误: {str(e)}"
202
+ logger.error(error_msg)
203
+ raise SmartRackApiException("响应数据格式错误", -500)
204
+
205
+ def _handle_response(self, response_data: Dict[str, Any], response_class=BaseResponse):
206
+ """
207
+ 处理API响应
208
+
209
+ Args:
210
+ response_data: 响应数据
211
+ response_class: 响应类
212
+
213
+ Returns:
214
+ 响应对象
215
+
216
+ Raises:
217
+ SmartRackApiException: API错误
218
+ """
219
+ try:
220
+ response = response_class.from_dict(response_data)
221
+
222
+ # 检查API返回的状态码
223
+ if response.code != 0:
224
+ error_msg = response.message or f"API错误 (代码: {response.code})"
225
+ raise SmartRackApiException(error_msg, response.code, response.session_id)
226
+
227
+ return response
228
+
229
+ except Exception as e:
230
+ if isinstance(e, SmartRackApiException):
231
+ raise
232
+ error_msg = f"响应处理错误: {str(e)}"
233
+ logger.error(error_msg)
234
+ raise SmartRackApiException(error_msg, -500)
235
+
236
+ # API方法实现
237
+
238
+ def clear_error(self, request: ClearErrorRequest) -> ClearErrorResponse:
239
+ """
240
+ 清错错误
241
+
242
+ Args:
243
+ request: 清错请求
244
+
245
+ Returns:
246
+ 清错响应
247
+ """
248
+ if not request.rack_id and not request.cell_id:
249
+ raise SmartRackValidationException("必须指定rackId或cellId")
250
+
251
+ response_data = self._make_request(
252
+ 'POST',
253
+ '/api/HJWMS/External/SmartRackExternal/ClearError',
254
+ request
255
+ )
256
+ return self._handle_response(response_data, ClearErrorResponse)
257
+
258
+ def rack_test(self, request: RackTestRequest) -> BaseResponse:
259
+ """
260
+ 料架测试
261
+
262
+ Args:
263
+ request: 测试请求
264
+
265
+ Returns:
266
+ 测试响应
267
+ """
268
+ if not request.rack_id:
269
+ raise SmartRackValidationException("rackId不能为空")
270
+
271
+ response_data = self._make_request(
272
+ 'POST',
273
+ '/api/HJWMS/External/SmartRackExternal/RackTest',
274
+ request
275
+ )
276
+ return self._handle_response(response_data)
277
+
278
+ def stock_in_request(self, request: StockInRequest) -> BaseResponse:
279
+ """
280
+ 入库请求
281
+
282
+ Args:
283
+ request: 入库请求
284
+
285
+ Returns:
286
+ 入库响应
287
+ """
288
+ if not request.sheet_id or not request.rack_id or not request.grn:
289
+ raise SmartRackValidationException("sheetId、rackId和grn不能为空")
290
+
291
+ response_data = self._make_request(
292
+ 'POST',
293
+ '/api/HJWMS/External/SmartRackExternal/StockInRequest',
294
+ request
295
+ )
296
+ return self._handle_response(response_data)
297
+
298
+ def stock_out_request(self, request: StockOutRequest) -> StockOutResponse:
299
+ """
300
+ 出库请求
301
+
302
+ Args:
303
+ request: 出库请求
304
+
305
+ Returns:
306
+ 出库响应
307
+ """
308
+ if not request.grn_list:
309
+ raise SmartRackValidationException("grnList不能为空")
310
+
311
+ response_data = self._make_request(
312
+ 'POST',
313
+ '/api/HJWMS/External/SmartRackExternal/StockOutRequest',
314
+ request
315
+ )
316
+ return self._handle_response(response_data, StockOutResponse)
317
+
318
+ def stock_out_sheet_cancel(self, request: StockOutSheetCancelRequest) -> StockOutResponse:
319
+ """
320
+ 单号出库取消
321
+
322
+ Args:
323
+ request: 取消请求
324
+
325
+ Returns:
326
+ 取消响应
327
+ """
328
+ if not request.sheet_id:
329
+ raise SmartRackValidationException("sheetId不能为空")
330
+
331
+ response_data = self._make_request(
332
+ 'POST',
333
+ '/api/HJWMS/External/SmartRackExternal/StockOutSheetCancel',
334
+ request
335
+ )
336
+ return self._handle_response(response_data, StockOutResponse)
337
+
338
+ def stock_out_sheet_finish(self, request: StockOutSheetFinishRequest) -> BaseResponse:
339
+ """
340
+ 单号出库完成
341
+
342
+ Args:
343
+ request: 完成请求
344
+
345
+ Returns:
346
+ 完成响应
347
+ """
348
+ if not request.sheet_id:
349
+ raise SmartRackValidationException("sheetId不能为空")
350
+
351
+ response_data = self._make_request(
352
+ 'POST',
353
+ '/api/HJWMS/External/SmartRackExternal/StockOutSheetFinish',
354
+ request
355
+ )
356
+ return self._handle_response(response_data)
357
+
358
+ def stock_out_sheet_request(self, request: StockOutSheetRequest) -> StockOutSheetResponse:
359
+ """
360
+ 单号出库请求
361
+
362
+ Args:
363
+ request: 出库请求
364
+
365
+ Returns:
366
+ 出库响应
367
+ """
368
+ if not request.sheet_id or not request.grn_list:
369
+ raise SmartRackValidationException("sheetId和grnList不能为空")
370
+
371
+ response_data = self._make_request(
372
+ 'POST',
373
+ '/api/HJWMS/External/SmartRackExternal/StockOutSheetRequest',
374
+ request
375
+ )
376
+ return self._handle_response(response_data, StockOutSheetResponse)
377
+
378
+ def reset_cell(self, request: ResetCellRequest) -> BaseResponse:
379
+ """
380
+ 重置储位状态
381
+
382
+ Args:
383
+ request: 重置请求
384
+
385
+ Returns:
386
+ 重置响应
387
+ """
388
+ if not request.cell_list:
389
+ raise SmartRackValidationException("cellList不能为空")
390
+
391
+ response_data = self._make_request(
392
+ 'POST',
393
+ '/api/HJWMS/External/SmartRackExternal/ResetCell',
394
+ request
395
+ )
396
+ return self._handle_response(response_data)
397
+
398
+ def stock_in_finish(self, request: StockInFinishRequest) -> BaseResponse:
399
+ """
400
+ 入库完成
401
+
402
+ Args:
403
+ request: 完成请求
404
+
405
+ Returns:
406
+ 完成响应
407
+ """
408
+ if not request.sheet_id:
409
+ raise SmartRackValidationException("sheetId不能为空")
410
+
411
+ response_data = self._make_request(
412
+ 'POST',
413
+ '/api/HJWMS/External/SmartRackExternal/StockInFinish',
414
+ request
415
+ )
416
+ return self._handle_response(response_data)
417
+
418
+ def get_stock_data(
419
+ self,
420
+ grn: Optional[str] = None,
421
+ rack_id: Optional[str] = None,
422
+ cell_id: Optional[str] = None,
423
+ stock_out_sheet: Optional[str] = None,
424
+ stock_in_sheet: Optional[str] = None,
425
+ led_color: Optional[LedColors] = None,
426
+ is_blink: Optional[bool] = None,
427
+ status: Optional[CellStatus] = None,
428
+ used: Optional[int] = None,
429
+ flag: Optional[int] = None,
430
+ sorting: Optional[str] = None,
431
+ skip_count: int = 0,
432
+ max_result_count: int = 10,
433
+ ) -> StockDataResponse:
434
+ """
435
+ 查询库存
436
+
437
+ Args:
438
+ grn: GRN
439
+ rack_id: 料架号
440
+ cell_id: 储位号
441
+ stock_out_sheet: 出库单号
442
+ stock_in_sheet: 入库单号
443
+ led_color: LED颜色
444
+ is_blink: 是否闪烁
445
+ status: 状态
446
+ used: 占用状态
447
+ flag: 特殊标识
448
+ sorting: 排序
449
+ skip_count: 跳过数量
450
+ max_result_count: 最大结果数量
451
+
452
+ Returns:
453
+ 库存数据响应
454
+ """
455
+ params = {
456
+ 'SkipCount': skip_count,
457
+ 'MaxResultCount': max_result_count,
458
+ }
459
+
460
+ # 添加可选参数
461
+ if grn:
462
+ params['GRN'] = grn
463
+ if rack_id:
464
+ params['RackId'] = rack_id
465
+ if cell_id:
466
+ params['CellId'] = cell_id
467
+ if stock_out_sheet:
468
+ params['StockOutSheet'] = stock_out_sheet
469
+ if stock_in_sheet:
470
+ params['StockInSheet'] = stock_in_sheet
471
+ if led_color is not None:
472
+ params['LedColor'] = led_color.value
473
+ if is_blink is not None:
474
+ params['IsBlink'] = is_blink
475
+ if status is not None:
476
+ params['Status'] = status.value
477
+ if used is not None:
478
+ params['Used'] = used
479
+ if flag is not None:
480
+ params['Flag'] = flag
481
+ if sorting:
482
+ params['Sorting'] = sorting
483
+
484
+ response_data = self._make_request(
485
+ 'GET',
486
+ '/api/HJWMS/External/SmartRackExternal/StockData',
487
+ params=params
488
+ )
489
+ return self._handle_response(response_data, StockDataResponse)
490
+
491
+ def get_stock_by_grn_list(self, request: GetStockByGRNListRequest) -> StockDataResponse:
492
+ """
493
+ 通过GRN列表查询库存
494
+
495
+ Args:
496
+ request: 查询请求
497
+
498
+ Returns:
499
+ 库存数据响应
500
+ """
501
+ if not request.grn_list:
502
+ raise SmartRackValidationException("grnList不能为空")
503
+
504
+ response_data = self._make_request(
505
+ 'POST',
506
+ '/api/HJWMS/External/SmartRackExternal/GetStockByGRNList',
507
+ request
508
+ )
509
+ return self._handle_response(response_data, StockDataResponse)
510
+
511
+ def close(self) -> None:
512
+ """关闭客户端"""
513
+ if self.session:
514
+ self.session.close()
515
+ logger.info("SmartRack客户端已关闭")
516
+
517
+ def __enter__(self):
518
+ """上下文管理器入口"""
519
+ return self
520
+
521
+ def __exit__(self, exc_type, exc_val, exc_tb):
522
+ """上下文管理器出口"""
523
+ self.close()
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 异常处理包
6
+ 包含所有SmartRack SDK相关的异常类
7
+ """
8
+
9
+ from .base import SmartRackException
10
+ from .api import SmartRackApiException
11
+ from .network import SmartRackNetworkException
12
+ from .configuration import SmartRackConfigurationException
13
+ from .validation import SmartRackValidationException
14
+
15
+ __all__ = [
16
+ "SmartRackException",
17
+ "SmartRackApiException",
18
+ "SmartRackNetworkException",
19
+ "SmartRackConfigurationException",
20
+ "SmartRackValidationException",
21
+ ]
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ API调用异常
6
+ """
7
+
8
+ from .base import SmartRackException
9
+
10
+
11
+ class SmartRackApiException(SmartRackException):
12
+ """API调用异常"""
13
+
14
+ def __init__(self, message: str, api_code: int, session_id: str = None):
15
+ """
16
+ 初始化异常
17
+
18
+ Args:
19
+ message: 错误消息
20
+ api_code: API响应代码
21
+ session_id: 会话ID
22
+ """
23
+ super().__init__(message, api_code)
24
+ self.api_code = api_code
25
+ self.session_id = session_id
26
+
27
+ def __str__(self) -> str:
28
+ return (f"SmartRackApiException(api_code={self.api_code}, "
29
+ f"session_id={self.session_id}, message={self.message})")
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ SmartRack SDK 异常基类
6
+ """
7
+
8
+
9
+ class SmartRackException(Exception):
10
+ """SmartRack SDK 异常基类"""
11
+
12
+ def __init__(self, message: str, error_code: int = -1):
13
+ """
14
+ 初始化异常
15
+
16
+ Args:
17
+ message: 错误消息
18
+ error_code: 错误代码
19
+ """
20
+ super().__init__(message)
21
+ self.error_code = error_code
22
+ self.message = message
23
+
24
+ def __str__(self) -> str:
25
+ return f"SmartRackException(code={self.error_code}, message={self.message})"
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 配置异常
6
+ """
7
+
8
+ from typing import Optional
9
+
10
+ from .base import SmartRackException
11
+
12
+
13
+ class SmartRackConfigurationException(SmartRackException):
14
+ """配置异常"""
15
+
16
+ def __init__(self, message: str, cause: Optional[Exception] = None):
17
+ """
18
+ 初始化异常
19
+
20
+ Args:
21
+ message: 错误消息
22
+ cause: 内部异常
23
+ """
24
+ super().__init__(message, -1)
25
+ self.cause = cause
26
+
27
+ def __str__(self) -> str:
28
+ return f"SmartRackConfigurationException(message={self.message})"
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 网络异常
6
+ """
7
+
8
+ from typing import Optional
9
+ from .base import SmartRackException
10
+
11
+
12
+ class SmartRackNetworkException(SmartRackException):
13
+ """网络异常"""
14
+
15
+ def __init__(self, message: str, status_code: Optional[int] = None, cause: Optional[Exception] = None):
16
+ """
17
+ 初始化异常
18
+
19
+ Args:
20
+ message: 错误消息
21
+ status_code: HTTP状态码
22
+ cause: 内部异常
23
+ """
24
+ super().__init__(message, status_code or -1)
25
+ self.status_code = status_code
26
+ self.cause = cause
27
+
28
+ @property
29
+ def is_timeout(self) -> bool:
30
+ """是否为超时异常"""
31
+ return (self.cause is not None and
32
+ hasattr(self.cause, '__class__') and
33
+ ('timeout' in str(type(self.cause)).lower() or
34
+ 'Timeout' in str(type(self.cause))))
35
+
36
+ @property
37
+ def is_connection_error(self) -> bool:
38
+ """是否为连接异常"""
39
+ return (self.cause is not None and
40
+ hasattr(self.cause, '__class__') and
41
+ ('Connection' in str(type(self.cause)) or
42
+ 'connect' in str(type(self.cause)).lower()))
43
+
44
+ def __str__(self) -> str:
45
+ parts = [f"SmartRackNetworkException"]
46
+ if self.status_code:
47
+ parts.append(f"status_code={self.status_code}")
48
+ if self.message:
49
+ parts.append(f"message={self.message}")
50
+ return f"({', '.join(parts)})"
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 验证异常
6
+ """
7
+
8
+ from typing import Optional
9
+
10
+ from .base import SmartRackException
11
+
12
+
13
+ class SmartRackValidationException(SmartRackException):
14
+ """验证异常"""
15
+
16
+ def __init__(self, message: str, cause: Optional[Exception] = None):
17
+ """
18
+ 初始化异常
19
+
20
+ Args:
21
+ message: 错误消息
22
+ cause: 内部异常
23
+ """
24
+ super().__init__(message, -1)
25
+ self.cause = cause
26
+
27
+ def __str__(self) -> str:
28
+ return f"SmartRackValidationException(message={self.message})"