views.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. # coding=utf-8
  2. import json
  3. import logging
  4. import datetime,time
  5. from mongoengine import DoesNotExist
  6. from apilib.monetary import VirtualCoin,RMB
  7. from django.views.decorators.http import require_POST
  8. from apilib.utils_datetime import to_datetime
  9. from apilib.utils_json import JsonResponse
  10. from apps.web.api.jn_north.constant import RESPONSE_CODE
  11. from apps.web.api.utils import AES_CBC_PKCS5padding_encrypt, AES_CBC_PKCS5padding_decrypt, generate_json_token, parse_json_token
  12. from apps.web.device.models import Group, Device, SwapGroup,Part
  13. from apps.web.south_intf.swap_carcharger import SwapContract
  14. from apps.web.dealer.models import Dealer
  15. from apps.web.agent.models import Agent
  16. from apps.web.core.helpers import ActionDeviceBuilder
  17. from apps.web.core.utils import async_operation
  18. from apps.web.user.models import ConsumeRecord, RechargeRecord,ServiceProgress
  19. from apps.web.user.utils import RechargeRecordBuilder
  20. logger = logging.getLogger(__name__)
  21. def queryToken(request):
  22. """
  23. 通过账号密码获取token
  24. """
  25. logger.debug("[queryToken] request body = {}".format(request.body))
  26. if not request.body:
  27. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1001)"})
  28. # 根据路径获取norther
  29. swapLabel = request.path.split('/')[3]
  30. norther = SwapContract.get_norther_by_label(swapLabel)
  31. if not norther:
  32. return None, JsonResponse({"Ret": RESPONSE_CODE.ERROR_PARAM, "Msg": u"请求的URL路径错误"})
  33. try:
  34. Data = json.loads(request.body).get("Data")
  35. data = json.loads(AES_CBC_PKCS5padding_decrypt(Data,norther.dataSecret2Us,norther.dataSecretIV2Us))
  36. except Exception as e:
  37. logger.exception(e)
  38. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1002)"})
  39. OperatorID = data.get("OperatorID")
  40. OperatorSecret = data.get("OperatorSecret")
  41. logger.debug("[queryToken] OperatorID = {}, OperatorSecret = {}".format(OperatorID, OperatorSecret))
  42. if not all((OperatorID, OperatorSecret)):
  43. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1003)"})
  44. try:
  45. norther = SwapContract.objects.filter(operatorId2Us=OperatorID, secret2Us=OperatorSecret).first() # type: SwapContract
  46. except DoesNotExist:
  47. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1004)"})
  48. except Exception as e:
  49. return JsonResponse({"Ret": RESPONSE_CODE.SYS_ERROR, "Msg": u"系统错误"})
  50. expire = 60 * 60 * 24 * 7
  51. result = {
  52. "OperatorID": norther.OperatorId,
  53. "SuccStat": 0,
  54. "AccessToken": norther.generate_json_token(data=norther.get_token_data(), expire=expire),
  55. "TokenAvailableTime": expire,
  56. "FailReason": None
  57. }
  58. logger.debug("[queryToken] return result = {}".format(result))
  59. # 拉取的时候加密 显式指明加密秘钥为 pull
  60. resultData = AES_CBC_PKCS5padding_encrypt(
  61. json.dumps(result),
  62. dataSecret=norther.dataSecret2Us,
  63. dataSecretIV=norther.dataSecretIV2Us
  64. )
  65. sig = norther.get_sig(resultData)
  66. return JsonResponse({
  67. "Ret": RESPONSE_CODE.SUCCESS,
  68. "Msg": u"请求成功",
  69. "Data": resultData,
  70. "Sig": sig
  71. })
  72. def check_and_get_norther(request):
  73. token = request.META.get('HTTP_AUTHORIZATION', "").replace("Bearer", "").strip()
  74. logger.info('[queryStationsInfo] , token = {}'.format(token))
  75. # 根据路径获取norther
  76. swapLabel = request.path.split('/')[3]
  77. norther = SwapContract.get_norther_by_label(swapLabel)
  78. if not norther:
  79. return None, JsonResponse({"Ret": RESPONSE_CODE.ERROR_PARAM, "Msg": u"请求的URL路径错误"})
  80. # 验证身份
  81. tokenData = norther.parse_json_token(token)
  82. if not tokenData:
  83. return None,JsonResponse({"Ret": RESPONSE_CODE.ERROR_TOKEN, "Msg": u"请求参数错误(1001)"})
  84. # 获取这个平台下面的所有的northers记录
  85. northers = SwapContract.get_norther(**tokenData)
  86. if not northers:
  87. return None,JsonResponse({"Ret": RESPONSE_CODE.ERROR_PARAM, "Msg": u"请求参数错误(1002)"})
  88. # 准备token所获取的参数信息
  89. if norther.id != northers.first().id:
  90. return None,JsonResponse({"Ret": RESPONSE_CODE.ERROR_TOKEN, "Msg": u"根据token获取数据和实际请求路径不一致(1002)"})
  91. return norther, None
  92. def get_request_data(request,norther):
  93. dataSecret2Us = norther.dataSecret2Us
  94. dataSecretIV2Us = norther.dataSecretIV2Us
  95. # 验证参数
  96. logger.debug("[queryStationsInfo] request body = {}".format(request.body))
  97. if not request.body:
  98. return None,JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1003)"})
  99. try:
  100. Data = json.loads(request.body).get("Data")
  101. data = json.loads(AES_CBC_PKCS5padding_decrypt(
  102. Data,
  103. dataSecret=dataSecret2Us,
  104. dataSecretIV=dataSecretIV2Us
  105. ))
  106. except Exception as e:
  107. logger.exception(e)
  108. return None,JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误(1004)"})
  109. return data,None
  110. def get_reply_reponse(result,norther):
  111. resultData = AES_CBC_PKCS5padding_encrypt(
  112. json.dumps(result),
  113. dataSecret=norther.dataSecret2Us,
  114. dataSecretIV=norther.dataSecretIV2Us
  115. )
  116. sig = norther.get_sig(resultData)
  117. return JsonResponse({"Ret": 0,"Msg": u"请求成功","Data": resultData,"Sig": sig})
  118. def queryStationsInfo(request):
  119. """
  120. 查询充电站的信息
  121. """
  122. logger.info('function into [queryStationsInfo]')
  123. norther,response = check_and_get_norther(request)
  124. if not norther:
  125. return response
  126. data,response = get_request_data(request, norther)
  127. if not data:
  128. return response
  129. # 分页以及查询参数
  130. pageNo = int(data.get('PageNo', 1))
  131. pageSize = int(data.get('PageSize', 10))
  132. lastQueryTime = data.get("LastQueryTime")
  133. logger.info('[queryStationsInfo],pageNo=%s,pageSize=%s,lastQueryTime=%s' % (pageNo,pageSize,lastQueryTime))
  134. # 查找出所有符合条件的信息
  135. dealerIds = []
  136. if norther.operatorType == 'dealer':
  137. dealerIds = [norther.operatorInnerId]
  138. elif norther.operatorType == 'agent':
  139. dealerIds = [str(dealer.id) for dealer in Dealer.objects(agentId = norther.operatorInnerId) ]
  140. elif norther.operatorType == 'manager':
  141. agentIds = [str(agent.id) for agent in Agent.objects(managerId = norther.operatorInnerId)]
  142. dealerIds = [str(dealer.id) for dealer in Dealer.objects(agentId__in = agentIds) ]
  143. if lastQueryTime:
  144. dateTime = to_datetime(lastQueryTime)
  145. swaps = SwapGroup.objects.filter(ownerId__in=dealerIds,swapFlag=True,deviceChangedTime__gte = dateTime,deviceNum__gt = 0)
  146. else:
  147. swaps = SwapGroup.objects.filter(ownerId__in=dealerIds,swapFlag=True,deviceNum__gt = 0)
  148. StationInfos = []
  149. for swap in swaps[(pageNo - 1) * pageSize:pageNo * pageSize]:
  150. StationInfos.append(norther.get_station(swap))
  151. result = {
  152. "PageNo": pageNo,
  153. "ItemSize": pageSize,
  154. "PageCount": swaps.count(),
  155. "StationInfos": StationInfos
  156. }
  157. logger.debug("[queryStationsInfo] return result = {}".format(result))
  158. return get_reply_reponse(result,norther)
  159. def queryStationStatus(request):
  160. logger.info('function into [queryStationStatus]')
  161. norther,response = check_and_get_norther(request)
  162. if not norther:
  163. return response
  164. data,response = get_request_data(request, norther)
  165. if not data:
  166. return response
  167. stationIDs = data.get("StationIDs")
  168. logger.info('[queryStationStatus],stationIDs=%s' % (','.join(stationIDs)))
  169. if not isinstance(stationIDs, list):
  170. return JsonResponse({"Ret": 4004, "Msg": u"系统错误"})
  171. StationStatusInfos = []
  172. for stationID in stationIDs:
  173. StationStatusInfos.append(
  174. {
  175. "StationID": stationID,
  176. "ConnectorStatusInfos": norther.get_connector_status_infos(stationID)
  177. }
  178. )
  179. result = {
  180. "StationStatusInfos": StationStatusInfos
  181. }
  182. logger.debug("[queryStationStatus] return result = {}".format(result))
  183. return get_reply_reponse(result, norther)
  184. def queryStationStats(request):
  185. """
  186. 取每个充电站在某个周期内的统计信息
  187. :param request:
  188. :return:
  189. """
  190. logger.info('function into [queryStationStats]')
  191. norther,response = check_and_get_norther(request)
  192. if not norther:
  193. return response
  194. data,response = get_request_data(request, norther)
  195. if not data:
  196. return response
  197. stationID = data.get("StationID")
  198. startTime = data.get("StartTime")
  199. endTime = data.get("EndTime")
  200. logger.info('[queryStationStats],stationID=%s,startTime=%s,endTime=%s' % (stationID,startTime,endTime))
  201. startTimeObj = datetime.datetime.strptime(startTime, "%Y-%m-%d")
  202. endTimeObj = datetime.datetime.strptime(endTime, "%Y-%m-%d")
  203. res = norther.get_station_state(stationID,startTimeObj, endTimeObj)
  204. res.update({
  205. "StationID": stationID,
  206. "StartTime": startTime,
  207. "EndTime": endTime
  208. })
  209. result = {"StationStats": res}
  210. logger.debug("[queryStationStats] return result = {}".format(result))
  211. return get_reply_reponse(result, norther)
  212. def queryEquipAuth(request):
  213. logger.info('function into [queryEquipAuth]')
  214. norther,response = check_and_get_norther(request)
  215. if not norther:
  216. return response
  217. data,response = get_request_data(request, norther)
  218. if not data:
  219. return response
  220. EquipAuthSeq = data.get('EquipAuthSeq')
  221. ConnectorID = data.get('ConnectorID')
  222. logger.info('[queryEquipAuth],EquipAuthSeq=%s,ConnectorID=%s' % (EquipAuthSeq,ConnectorID))
  223. part = Part.objects(id = ConnectorID).first()
  224. if not part:
  225. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  226. dev = Device.get_dev_by_l(part.logicalCode)
  227. box = ActionDeviceBuilder.create_action_device(dev)
  228. FailReason = 0
  229. SuccStat = 0
  230. try:
  231. devInfo = box.get_port_status_from_dev()
  232. portInfo = devInfo.get(str(part.partNo),{})
  233. if portInfo['isPlugin'] == 'no':
  234. FailReason = 1
  235. except Exception,e:
  236. FailReason = 2
  237. if FailReason != 0:
  238. SuccStat = 1
  239. result = {
  240. 'EquipAuthSeq':EquipAuthSeq,
  241. 'ConnectorID':ConnectorID,
  242. 'SuccStat':SuccStat,
  243. 'FailReason':FailReason
  244. }
  245. logger.debug("[queryEquipAuth] return result = {}".format(result))
  246. return get_reply_reponse(result, norther)
  247. def queryEquipBusinessPolicy(request):
  248. """
  249. 用于查询运营商的充电设备接口计费模型信息
  250. :param request:
  251. :return:
  252. """
  253. logger.info('function into [queryEquipBusinessPolicy]')
  254. norther,response = check_and_get_norther(request)
  255. if not norther:
  256. return response
  257. data,response = get_request_data(request, norther)
  258. if not data:
  259. return response
  260. equipBizSeq = data.get("EquipBizSeq")
  261. connectorID = data.get("ConnectorID")
  262. logger.info('[queryEquipBusinessPolicy],equipBizSeq=%s,connectorID=%s' % (equipBizSeq,connectorID))
  263. part = Part.objects(id = connectorID).first()
  264. if not part:
  265. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  266. dev = Device.get_dev_by_l(part.logicalCode)
  267. devObj = Device.objects.get(devNo = dev['devNo'])
  268. feeMode = devObj.otherConf.get('feeMode',{})
  269. shiduan = feeMode.get('shiduan','000000000000000000000000000000000000000000000000')
  270. elecFeeDict = {'0':feeMode.get('jianFee',0),'1':feeMode.get('fengFee',0),'2':feeMode.get('pingFee',0),'3':feeMode.get('guFee',0)}
  271. serveFeeDict = {'0':feeMode.get('jianServeFee',0),'1':feeMode.get('fengServeFee',0),'2':feeMode.get('pingServeFee',0),'3':feeMode.get('guServeFee',0)}
  272. PolicyInfos = []
  273. for ii in range(48):
  274. startHour = 0 + ii/2
  275. startMin = '00' if ii%2==0 else '30'
  276. startTime = '%02d%s00' % (startHour,startMin)
  277. if (ii == 0) or (ii > 0 and shiduan[ii] != shiduan[ii-1]):
  278. PolicyInfos.append({'StartTime':startTime,'ElecPrice':elecFeeDict.get(shiduan[ii]),'SevicePrice':serveFeeDict.get(shiduan[ii])})
  279. result = {
  280. "EquipBizSeq": equipBizSeq,
  281. "ConnectorID": connectorID,
  282. "SuccStat": 0 if PolicyInfos else 1,
  283. "FailReason": 0 if PolicyInfos else 1,
  284. "SumPeriod": 1,
  285. "PolicyInfos": PolicyInfos
  286. }
  287. logger.debug("[queryEquipBusinessPolicy] return result = {}".format(result))
  288. return get_reply_reponse(result, norther)
  289. def query_start_charge(request):
  290. logger.info('function into [query_start_charge]' )
  291. norther,response = check_and_get_norther(request)
  292. if not norther:
  293. return response
  294. data,response = get_request_data(request, norther)
  295. if not data:
  296. return response
  297. StartChargeSeq = data.get("StartChargeSeq")
  298. connectorID = data.get("ConnectorID")
  299. logger.info('[query_start_charge],StartChargeSeq=%s,connectorID=%s' % (StartChargeSeq,connectorID))
  300. part = Part.objects(id = connectorID).first()
  301. if not part:
  302. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  303. dev = Device.get_dev_by_l(part.logicalCode)
  304. if not dev:
  305. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  306. group = Group.get_group(dev['groupId'])
  307. # 启动设备,这里以能湃的
  308. box = ActionDeviceBuilder.create_action_device(dev)
  309. SuccStat,FailReason,orderNo = box.start_device_swap(part.partNo)
  310. if SuccStat != 0:
  311. result = {
  312. "StartChargeSeq": StartChargeSeq,
  313. "StartChargeSeqStat": 5,
  314. "connectorID": connectorID,
  315. "SuccStat": SuccStat,
  316. "FailReason": FailReason,
  317. }
  318. # 异步通知北向
  319. async_operation(norther.notification_start_charge_result,result)
  320. logger.debug("[query_start_charge] return result = {}".format(result))
  321. return get_reply_reponse(result, norther)
  322. # 新建一条充值订单
  323. rechargeOrder = RechargeRecordBuilder.new_swap_recharge(norther.swapLabel,dev,group,part.partNo,StartChargeSeq,orderNo)
  324. # 记录consume rcd
  325. newRecord = {
  326. 'orderNo': orderNo,
  327. 'openId': '',
  328. 'nickname': '',
  329. 'coin': VirtualCoin(0),
  330. 'money': RMB(0),
  331. 'devNo': dev['devNo'],
  332. 'logicalCode': dev['logicalCode'],
  333. 'groupId': dev['groupId'],
  334. 'ownerId': dev['ownerId'],
  335. 'address': group['address'],
  336. 'groupNumber': dev['groupNumber'],
  337. 'groupName': group['groupName'],
  338. 'devTypeName': dev.devTypeName,
  339. 'devTypeCode': dev.devTypeCode,
  340. 'startKey': '',
  341. 'isNormal': True,
  342. 'errorDesc': '',
  343. 'sequanceNo': orderNo,
  344. 'status': ConsumeRecord.Status.CREATED,
  345. 'package': {},
  346. 'remarks':u'互联互通',
  347. 'rechargeRcdId':str(rechargeOrder.id)
  348. }
  349. consumeOrder = ConsumeRecord(**newRecord)
  350. consumeOrder.save() # type: ConsumeRecord
  351. result = {
  352. "StartChargeSeq": StartChargeSeq,
  353. "StartChargeSeqStat": 2,
  354. "connectorID": connectorID,
  355. "SuccStat": SuccStat,
  356. "FailReason": FailReason,
  357. }
  358. new_service_progress = ServiceProgress(
  359. device_imei = dev['devNo'],
  360. devTypeCode = dev['devType']['code'],
  361. port = part.partNo,
  362. attachParas = {'StartChargeSeq':StartChargeSeq,'connectorID':connectorID},
  363. start_time = int(time.time()),
  364. finished_time = int(time.time()) + 3600*24,
  365. status = 'waiting', # 等充电事件上报后,再刷新此状态
  366. consumeOrder = {},
  367. weifuleOrderNo = orderNo,
  368. expireAt = datetime.datetime.now() + datetime.timedelta(days = 91))
  369. new_service_progress.save()
  370. # 异步通知北向
  371. async_operation(norther.notification_start_charge_result,result)
  372. logger.debug("[query_start_charge] return result = {}".format(result))
  373. return get_reply_reponse(result, norther)
  374. def query_equip_charge_status(request):
  375. logger.info('function into [query_equip_charge_status]' )
  376. norther,response = check_and_get_norther(request)
  377. if not norther:
  378. return response
  379. data,response = get_request_data(request, norther)
  380. if not data:
  381. return response
  382. StartChargeSeq = data.get("StartChargeSeq")
  383. logger.info('[query_equip_charge_status],StartChargeSeq=%s' % (StartChargeSeq))
  384. rechargeRcd = RechargeRecord.objects(wxOrderNo = StartChargeSeq).first()
  385. if not rechargeRcd:
  386. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  387. part = Part.objects(logicalCode = rechargeRcd.logicalCode,partNo = rechargeRcd.extraInfo['portNo']).first()
  388. if not part:
  389. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  390. dev = Device.get_dev_by_l(part.logicalCode)
  391. if not dev:
  392. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  393. box = ActionDeviceBuilder.create_action_device(dev)
  394. result = box.get_charge_status_for_swap(part.partNo,str(part.id))
  395. result.update({
  396. 'StartChargeSeq':StartChargeSeq,
  397. 'ConnectorId':str(part.id),
  398. 'StartTime':rechargeRcd.time,
  399. 'EndTime':datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  400. })
  401. logger.debug("[query_equip_charge_status] return result = {}".format(result))
  402. return get_reply_reponse(result, norther)
  403. def query_stop_charge(request):
  404. logger.info('function into [query_stop_charge]' )
  405. norther,response = check_and_get_norther(request)
  406. if not norther:
  407. return response
  408. data,response = get_request_data(request, norther)
  409. if not data:
  410. return response
  411. StartChargeSeq = data.get("StartChargeSeq")
  412. connectorID = data.get("ConnectorID")
  413. logger.info('[query_stop_charge],StartChargeSeq=%s,connectorID=%s' % (StartChargeSeq,connectorID))
  414. part = Part.objects(id = connectorID).first()
  415. if not part:
  416. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  417. dev = Device.get_dev_by_l(part.logicalCode)
  418. if not dev:
  419. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_POST, "Msg": u"请求参数错误,没有找到对应的设备接口"})
  420. group = Group.get_group(dev['groupId'])
  421. # 首先检查下数据库订单情况,是否已经停止了
  422. rechargeRcd = RechargeRecord.objects(wxOrderNo = StartChargeSeq).first()
  423. if not rechargeRcd:
  424. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_PARAM, "Msg": u"请求参数错误,没有找到对应的充值订单"})
  425. consumeRcd = ConsumeRecord.objects(orderNo = rechargeRcd.extraInfo['consumeOrderNo']).first()
  426. if not consumeRcd:
  427. return JsonResponse({"Ret": RESPONSE_CODE.ERROR_PARAM, "Msg": u"请求参数错误,没有找到对应的消费订单"})
  428. if consumeRcd.status == ConsumeRecord.Status.FINISHED:
  429. result = {
  430. "StartChargeSeq": StartChargeSeq,
  431. "StartChargeSeqStat": 4, # 已经结束
  432. "connectorID": connectorID,
  433. "SuccStat": 0,
  434. "FailReason": 0,
  435. }
  436. # 启动异步,通知启动设备成功
  437. async_operation(norther.notification_stop_charge_result,result)
  438. return get_reply_reponse(result, norther)
  439. box = ActionDeviceBuilder.create_action_device(dev)
  440. SuccStat,FailReason = box.stop_device_swap(part.partNo)
  441. result = {
  442. "StartChargeSeq": StartChargeSeq,
  443. "StartChargeSeqStat": 4,
  444. "connectorID": connectorID,
  445. "SuccStat": SuccStat,
  446. "FailReason": FailReason,
  447. }
  448. # 启动异步,通知启动设备成功
  449. async_operation(norther.notification_stop_charge_result,result)
  450. logger.debug("[query_stop_charge] return result = {}".format(result))
  451. return get_reply_reponse(result, norther)