import 'dart:async'; import 'dart:core'; import 'dart:typed_data'; import 'package:app_business/app_config.dart'; import 'package:app_business/generated/base.pb.dart' as pb; import 'package:app_business/generated/google/protobuf/duration.pb.dart' as pb; import 'package:app_business/generated/google/protobuf/timestamp.pb.dart' as pb; import 'package:app_business/generated/track_offical.pbgrpc.dart' as pb; import 'package:app_business/service/app.dart'; import 'package:grpc/grpc.dart'; import 'package:track_common/model.dart'; import 'package:track_common/track_common.dart'; import 'abase.dart'; export 'package:app_business/generated/base.pb.dart' show DefaultRequest, IdRequest, IdArrRequest; export 'package:app_business/generated/google/protobuf/timestamp.pb.dart'; export 'package:app_business/generated/track_offical.pbgrpc.dart' show ToMatchRegusterAddRequest, ToMatchRegusterListRequest; export 'package:fixnum/fixnum.dart' show Int64; typedef SmsType = pb.SmsType; extension _ImageExt on pb.NetImage { NetImage toModel() { return NetImage(url, md5); } } extension DateTimeExt on DateTime? { pb.Timestamp toPb() { if (this != null) { return pb.Timestamp.fromDateTime(this!.toUtc()); } else { return pb.Timestamp(); } } } extension TSExt on pb.Timestamp { DateTime toModel() { return toDateTime(toLocal: true); } } extension DurationExt on pb.Duration { Duration toModel() { final microseconds = hasNanos() ? nanos / 1000 : 0; return Duration( seconds: seconds.toInt(), microseconds: microseconds.toInt()); } } extension PositionExt on pb.Position { Position toModel() { return Position(latitude: latitude, longitude: longitude) ..altitude = altitude; } } extension PBToControlPointExt on pb.ToControlPoint { ControlPoint toModel() { final one = ControlPoint() ..areaId = sn ..intId = id ..isSuccess = isCheckSuccess ..sn = orderNo.toString() ..checkAfterPrev = checkAfterLast.toModel() ..checkAfterStart = checkAfterStart.toModel() ..checkDistanceAfterPrev = disAfterLast.meter ..distanceStraightToPrev = disStraightAfterLast.meter ..paceAfterPrev = Pace.perKm(paceAfterLast.seconds) ..paceAfterStart = Pace.perKm(paceAfterStart.seconds) ..position = ciPosition.toModel(); if (cType == pb.CType.BeginType) { one.isStart = true; } if (cType == pb.CType.EndType) { one.isFinish = true; } return one; } } class ApiService extends IService { ClientChannel? channel; @override Future init() async { channel = _newChannel(); } ClientChannel _newChannel() { return ClientChannel( AppConfig.apiHost, port: AppConfig.apiPort, options: const ChannelOptions(credentials: ChannelCredentials.secure() // ChannelCredentials.insecure() ), ); } pb.ApiToClient get stub { return _newStub(timeout: 10.seconds); } pb.ApiToClient _newStub({Duration? timeout, ClientChannel? channel}) { if (this.channel == null) { throw Exception('$runtimeType 未初始化'); } final metadata = {}; metadata['version'] = _appVersion; if (token != null) { metadata['token'] = token!; } // debug("token: $token"); return pb.ApiToClient(channel ?? this.channel!, options: CallOptions( metadata: metadata, timeout: timeout, )); } String get _appVersion => Get.find().appVersion; set token(String? v) { Get.find().token.val = v ?? ''; } String? get token { final app = Get.find(); final token = app.token.val; if (token.isEmpty) { return null; } return token; } // 获取短信验证码 Future authSendCodeToPhone(String phone, SmsType smsType) async { info('authSendCodeToPhone [$phone]'); await stub.toSendCodeToPhoneV2(pb.ToSendCodeToPhoneRequestV2() ..phone = phone ..smsType = smsType); } // 场控端_登录 Future signIn(String userCode, String password, String ip) async { final r = await stub.toSignInV2(pb.ToSignInRequestV2() ..userCode = userCode ..password = password ..ip = ip); token = r.token; debug('sign in success: $token'); } // 场控端_登出 void signOut() { stub.toSignOutV2(pb.DefaultRequest()); token = null; } Future getSmsSendLeftTime(String phone) async { final r = await stub .toGetSmsSendLeftTimeV2(pb.GetSmsSendLeftTimeRequest()..phone = phone); info('getSmsSendLeftTime: $phone - ${r.second}s'); return r.second.seconds; } Future> getMapList(int limit, int offset) async { final r = await stub.toMapListV2(pb.MapListRequestV2() ..limit = limit ..offset = offset); return r.list .map((e) => MapInfo(e.mapId, e.name, e.distance.meter, e.description, e.mapScaleNumber, e.image.toModel())) .toList(); } Future getBinReaderByMd5(Uint8List md5) async { final stream = stub.toGetBinaryByMd5(pb.ToGetBinaryByMd5Request()..md5 = md5); final controller = StreamController>(); controller.onCancel = () { stream.cancel(); }; Future rcv() async { try { await for (final one in stream) { controller.add(one.data); } } finally { controller.close(); stream.cancel(); } } rcv(); stream.headers.then((value) => debug(value)); final headers = await stream.headers; final lenStr = headers['all-length']!; final length = int.parse(lenStr); final nonce = headers['nonce']!; final ext = headers['ext']!; return BinReader( data: controller.stream, length: length, ext: ext, nonce: nonce); } Future eventEdit(int id, EventRegisterInfo event) async { await stub.toMatchRegusterEdit(pb.ToMatchRegusterEditRequest() ..id = id ..regName = event.name ..startAt = event.startAt.toPb() ..stopAt = event.stopAt.toPb() ..isQueryPwd = event.password != null ..queryPasswd = event.password ?? ''); } } class EventRegisterInfo { var id = 0; var name = ''; var startAt = DateTime.now(); var stopAt = DateTime.now(); String? password; }