import { InjectClient, Provide, Inject } from '@midwayjs/core';
|
import { BaseService } from '@cool-midway/core';
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
import { Repository } from 'typeorm';
|
import { PushWeightEntity } from '../entity/weight';
|
import { PushSiteweightEntity } from '../entity/siteweight';
|
import { PushAuditlogEntity } from '../entity/auditlog';
|
import { PushCameraGpsEntity } from '../entity/cameragps';
|
import { PushVehicleGpsEntity } from '../entity/vehiclegps';
|
import { PushDeviceOnoffEntity } from '../entity/deviceonoff';
|
import { PushDevicealarmEntity } from '../entity/devicealarm';
|
import { PushIsapiRawEntity } from '../entity/isapientity';
|
import { IsapiEventType } from '../entity/isapieventtype';
|
import { PluginService } from '../../plugin/service/info';
|
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
|
|
/**
|
* 开放接口业务逻辑服务
|
*/
|
@Provide()
|
export class PushOpenService extends BaseService {
|
@InjectEntityModel(PushWeightEntity)
|
pushWeightEntity: Repository<PushWeightEntity>;
|
|
@InjectEntityModel(PushSiteweightEntity)
|
pushSiteweightEntity: Repository<PushSiteweightEntity>;
|
|
@InjectEntityModel(PushCameraGpsEntity)
|
pushCameraGpsEntity: Repository<PushCameraGpsEntity>;
|
|
@InjectEntityModel(PushVehicleGpsEntity)
|
pushVehicleGpsEntity: Repository<PushVehicleGpsEntity>;
|
|
@InjectEntityModel(PushDeviceOnoffEntity)
|
pushDeviceOnoffEntity: Repository<PushDeviceOnoffEntity>;
|
|
@InjectEntityModel(PushDevicealarmEntity)
|
pushDevicealarmEntity: Repository<PushDevicealarmEntity>;
|
|
@InjectEntityModel(PushAuditlogEntity)
|
pushAuditlogEntity: Repository<PushAuditlogEntity>;
|
|
@InjectEntityModel(PushIsapiRawEntity)
|
pushIsapiRawEntity: Repository<PushIsapiRawEntity>;
|
|
@Inject()
|
pluginService: PluginService;
|
|
@InjectClient(CachingFactory, 'default')
|
midwayCache: MidwayCache;
|
|
/**
|
* 上报称重数据核心业务
|
*/
|
async reportWeight(data: any) {
|
const logTime = new Date();
|
|
try {
|
// 1. 保存原始称重数据
|
const rawWeight = new PushWeightEntity();
|
const rawWeightValue = data.garbageWeight || 0;
|
const processedWeight = this.calcProcessedWeight(data.garbageType, rawWeightValue);
|
rawWeight.deviceNo = data.deviceNo;
|
// rawWeight.uploadTime = data.uploadTime ? new Date(data.uploadTime) : new Date();
|
rawWeight.uploadTime = new Date();
|
rawWeight.garbageType = data.garbageType;
|
rawWeight.weight = processedWeight;
|
rawWeight.extraData =
|
typeof data.extraData === 'object'
|
? JSON.stringify(data.extraData)
|
: data.extraData;
|
await this.pushWeightEntity.save(rawWeight);
|
|
// 2. 模拟关联基础数据模块查询获取商户信息并写入业务称重表
|
const siteWeight = new PushSiteweightEntity();
|
siteWeight.deviceNo = data.deviceNo;
|
siteWeight.uploadTime = rawWeight.uploadTime;
|
siteWeight.garbageType = data.garbageType;
|
siteWeight.weight = processedWeight;
|
siteWeight.extraData = rawWeight.extraData;
|
|
// 动态查询设备基础数据关联信息
|
const iotDevices = await this.nativeQuery(
|
'SELECT businessId, departmentId FROM t_basicdata_iot WHERE iotCode = ? LIMIT 1',
|
[data.deviceNo],
|
);
|
|
if (iotDevices && iotDevices.length > 0) {
|
siteWeight.businessId = iotDevices[0].businessId;
|
siteWeight.departmentId = iotDevices[0].departmentId;
|
await this.pushSiteweightEntity.save(siteWeight);
|
} else {
|
siteWeight.businessId = 'UNKNOWN';
|
siteWeight.departmentId = -1;
|
await this.writePushAuditLog("weight", { "deviceNo": data.deviceNo, "requestContent": data, "errorMessage": "电子秤还未分配到垃圾收集点." });
|
}
|
|
} catch (err) {
|
await this.writePushAuditLog("weight", { "deviceNo": data.deviceNo || "UNKNOW", "requestContent": data, "errorMessage": err });
|
}
|
|
}
|
|
/**
|
* 接收萤石云 消息并缓存
|
*/
|
async saveYingshiyunMessage(body: any) {
|
const messageId = body?.header?.messageId || Math.random().toString(36).substring(2);
|
// await this.midwayCache.set(`push:yingshiyun:queue:${messageId}`, JSON.stringify(body), 24 * 3600);
|
await this.midwayCache.set(`push:yingshiyun:queue:${messageId}`, JSON.stringify(body), 24 * 3600 * 1000);
|
this.processYingshiyunAsync(messageId).catch(err => {
|
console.error('异步处理GPS消息失败:', err);
|
});
|
return messageId;
|
}
|
|
/**
|
* 异步解析萤石云消息,按照消息类型 入库
|
*/
|
async processYingshiyunAsync(messageId: string) {
|
const dataStr = await this.midwayCache.get(`push:yingshiyun:queue:${messageId}`) as string | null;
|
if (!dataStr) return;
|
const body = JSON.parse(dataStr);
|
const type = body?.header?.type;
|
console.log(`---------------------------------${type}`);
|
try {
|
switch (type) {
|
// 设备上下线
|
case 'ys.onoffline': {
|
const onoffData: any = {
|
deviceId: body.header.deviceId,
|
channelNo: Number(body.header.channelNo) || 0,
|
messageId: body.header.messageId,
|
messageTime: new Date(Number(body.header.messageTime)),
|
msgType: body.body.msgType,
|
devType: body.body.devType || '',
|
deviceName: body.body.deviceName || '',
|
subSerial: body.body.subSerial || '',
|
dasId: body.body.dasId || '',
|
natIp: body.body.natIp || '',
|
occurTime: body.body.occurTime ? new Date(body.body.occurTime.replace(' ', 'T')) : null,
|
regTime: body.body.regTime ? new Date(body.body.regTime.replace(' ', 'T')) : null,
|
};
|
await this.pushDeviceOnoffEntity.save(onoffData);
|
break;
|
}
|
|
// 萤石告警
|
case 'ys.alarm': {
|
const alarmData: any = {
|
devSerial: body.body.devSerial,
|
alarmId: body.body.alarmId,
|
alarmTime: body.body.alarmTime ? new Date(body.body.alarmTime.replace('T', ' ')) : new Date(),
|
alarmType: body.body.alarmType,
|
channelName: body.body.channelName,
|
channelType: Number(body.body.channelType) || 1,
|
customInfo: body.body.customInfo || '',
|
customType: body.body.customType || '',
|
describe: body.body.describe || '',
|
location: body.body.location || '',
|
relationId: body.body.relationId || '',
|
status: Number(body.body.status) || 1,
|
channelNo: String(body.body.channel ?? body.body.channelNo ?? ''),
|
};
|
if (body.body.checksum) alarmData.checksum = body.body.checksum;
|
if (body.body.crypt !== undefined) alarmData.crypt = Number(body.body.crypt);
|
// 图片处理(有则处理,无则跳过)
|
const localUrls: Record<string, string>[] = [];
|
if (body.body.pictureList?.length) {
|
try {
|
const filePlugin = await this.pluginService.getInstance('upload');
|
for (const item of body.body.pictureList) {
|
try {
|
const result = await filePlugin.downAndUpload(item.url);
|
localUrls.push({ url: result, id: item.id });
|
} catch (err) {
|
console.error(`图片下载失败: ${item.url}`, err);
|
}
|
}
|
} catch (err) {
|
console.error('filePlugin init failed:', err);
|
}
|
}
|
alarmData.pictureList = localUrls;
|
await this.pushDevicealarmEntity.save(alarmData);
|
break;
|
}
|
//海康 ISAPI
|
case 'ys.open.isapi': {
|
const header = body.header;
|
const payloadStr = body.body?.payload;
|
if (!payloadStr) {
|
console.warn('ISAPI payload missing');
|
break;
|
}
|
let parsed: any;
|
try {
|
parsed = JSON.parse(payloadStr);
|
} catch (e) {
|
console.error('ISAPI payload parse error', e);
|
break;
|
}
|
|
// 按 eventType 分发
|
const eventType = parsed.eventType || parsed.ISAPI?.Intelligent?.ActiveSafety?.EventType || '';
|
switch (eventType) {
|
case 'VehicleGPS':
|
await this.processCameraGpsFromIsapi(parsed, header);
|
break;
|
case 'ActiveSafety':
|
case 'intelligentDetection':
|
await this.processActiveSafetyFromIsapi(parsed, header);
|
break;
|
case 'violentDriving':
|
case 'harshAcceleration':
|
case 'harshBraking':
|
case 'sharpTurn':
|
await this.processGsensorFromIsapi(parsed, header);
|
break;
|
default:
|
// 设备状态等未知类型,存 raw(你原来的逻辑)
|
const mappedType = Object.values(IsapiEventType).includes(eventType)
|
? eventType
|
: IsapiEventType.UNKNOWN;
|
await this.pushIsapiRawEntity.save({
|
messageId: header.messageId,
|
deviceId: header.deviceId,
|
channelNo: header.channelNo ?? 0,
|
eventType: mappedType,
|
eventState: parsed.eventState,
|
rawPayload: payloadStr,
|
parsed,
|
eventTime: parsed.dateTime
|
? new Date(parsed.dateTime)
|
: new Date(Number(header.messageTime)),
|
});
|
}
|
break;
|
}
|
default:
|
console.warn('Unknown yingshi webhook type:', type);
|
}
|
} catch (err) {
|
await this.writePushAuditLog(type || 'unknown', {
|
deviceNo: body?.sn || 'UNKNOWN',
|
requestContent: body,
|
errorMessage: err,
|
});
|
}
|
// 不管成功与否,删除缓存中的任务队列
|
await this.midwayCache.del(`push:yingshiyun:queue:${messageId}`);
|
}
|
|
|
|
/**
|
* 异步解析车载监控的GPS数据,存储并添加业务表
|
*/
|
async processCameraGpsAsync(body: any) {
|
try {
|
let vehicleNo = '';
|
if (body.sn) {
|
|
const gpsData = body.data || {};
|
const statusVal = gpsData.status === 'A' ? 'A' : 'B';
|
|
await this.pushCameraGpsEntity.save({
|
sn: body.sn,
|
msgSeq: body.msgSeq,
|
createDate: body.createDate && !isNaN(Number(body.createDate))
|
? new Date(Number(body.createDate))
|
: new Date(),
|
gpsTime: gpsData.gpsTime
|
? new Date(gpsData.gpsTime.replace(' ', 'T'))
|
: new Date(),
|
longitude: gpsData.longitude,
|
latitude: gpsData.latitude,
|
speed: gpsData.speed,
|
direction: gpsData.direction,
|
elevation: gpsData.elevation,
|
status: statusVal,
|
});
|
|
const iotDevice: any[] = await this.nativeQuery(
|
'SELECT businessId FROM t_basicdata_iot WHERE iotCode = ? LIMIT 1',
|
[body.sn]
|
);
|
if (iotDevice && iotDevice.length > 0 && iotDevice[0].businessId) {
|
vehicleNo = `vehicle_${iotDevice[0].businessId}`;
|
|
await this.pushVehicleGpsEntity.save({
|
sn: body.sn,
|
msgSeq: body.msgSeq,
|
createDate: body.createDate && !isNaN(Number(body.createDate))
|
? new Date(Number(body.createDate))
|
: new Date(),
|
vehicleNo,
|
gpsTime: gpsData.gpsTime
|
? new Date(gpsData.gpsTime.replace(' ', 'T'))
|
: new Date(),
|
longitude: gpsData.longitude,
|
latitude: gpsData.latitude,
|
speed: gpsData.speed,
|
direction: gpsData.direction,
|
elevation: gpsData.elevation,
|
status: statusVal,
|
});
|
|
} else {
|
await this.writePushAuditLog("camera_gps", { "deviceNo": body.sn, "requestContent": body, "errorMessage": "车载监控还未分配到车辆" });
|
}
|
} else {
|
await this.writePushAuditLog("camera_gps", { "deviceNo": "UNKOWN", "requestContent": body, "errorMessage": "未包含设备号SN" });
|
}
|
|
} catch (err: any) {
|
|
await this.writePushAuditLog("camera_gps", { "deviceNo": body.sn || "UNKNOW", "requestContent": body, "errorMessage": err });
|
|
}
|
}
|
|
async writePushAuditLog(pushType: string, data: any) {
|
let isSuccess = true;
|
let errorMsg = '';
|
try {
|
const auditLog = new PushAuditlogEntity();
|
auditLog.logTime = new Date();
|
auditLog.pushType = pushType;
|
auditLog.deviceNo = data.deviceNo || data.deviceSn || data.sn || 'unknown-device';
|
auditLog.requestContent = typeof data.requestContent === 'string' ? data.requestContent : JSON.stringify(data.requestContent);
|
auditLog.errorMessage = data.errorMessage || 'unknown-erroe';
|
await this.pushAuditlogEntity.save(auditLog);
|
} catch (err) {
|
isSuccess = false;
|
}
|
|
}
|
|
/**
|
* 保存摄像头主动上报告警事件
|
* @param alarmData
|
*/
|
async saveCameraAlarmEvent(alarmData: any) {
|
const { eventType, targetType, ipAddress, dateTime } = alarmData;
|
if (eventType === 'VMD') {
|
console.log(`【移动侦测告警】设备${ipAddress},时间:${dateTime},目标类型:${targetType}`);
|
// TODO: 完成摄像机侦测数据保存
|
} else {
|
console.log(`【未知摄像头事件类型】type:${eventType}`);
|
}
|
return Date.now();
|
}
|
|
/**
|
* 从 ISAPI VehicleGPS 解析并写入车载GPS表
|
*/
|
async processCameraGpsFromIsapi(parsed: any, header: any) {
|
try {
|
const gps = parsed.GPS || parsed.ISAPI?.VehicleGPS?.GPS || {};
|
if (!gps.Longitude || !gps.Latitude) {
|
console.warn('VehicleGPS payload missing coordinates, skip');
|
return;
|
}
|
|
const gpsTime = gps.PositionTime || parsed.dateTime;
|
await this.pushCameraGpsEntity.save({
|
sn: header.deviceId, // 萤石 deviceId = 设备序列号
|
msgSeq: header.messageId || '',
|
createDate: new Date(Number(header.messageTime) || Date.now()),
|
gpsTime: gpsTime ? new Date(gpsTime.replace(' ', 'T')) : new Date(),
|
longitude: Number(gps.Longitude),
|
latitude: Number(gps.Latitude),
|
speed: gps.Speed != null ? Number(gps.Speed) : 0,
|
direction: gps.Direction != null ? Number(gps.Direction) : 0,
|
elevation: gps.Altitude != null ? Number(gps.Altitude) : 0,
|
status: gps.Status?.GPSFixed === false ? 'B' : 'A',
|
});
|
|
// 同步写业务车辆GPS表(复用你已有的逻辑)
|
const iotDevice: any[] = await this.nativeQuery(
|
'SELECT businessId FROM t_basicdata_iot WHERE iotCode = ? LIMIT 1',
|
[header.deviceId]
|
);
|
if (iotDevice?.length > 0 && iotDevice[0].businessId) {
|
const vehicleNo = `vehicle_${iotDevice[0].businessId}`;
|
await this.pushVehicleGpsEntity.save({
|
sn: header.deviceId,
|
msgSeq: header.messageId || '',
|
createDate: new Date(Number(header.messageTime) || Date.now()),
|
vehicleNo,
|
gpsTime: gpsTime ? new Date(gpsTime.replace(' ', 'T')) : new Date(),
|
longitude: Number(gps.Longitude),
|
latitude: Number(gps.Latitude),
|
speed: gps.Speed != null ? Number(gps.Speed) : 0,
|
direction: gps.Direction != null ? Number(gps.Direction) : 0,
|
elevation: gps.Altitude != null ? Number(gps.Altitude) : 0,
|
status: gps.Status?.GPSFixed === false ? 'B' : 'A',
|
});
|
}
|
} catch (err) {
|
console.error('processCameraGpsFromIsapi error:', err);
|
await this.writePushAuditLog('isapi_vehicle_gps', {
|
deviceNo: header.deviceId,
|
requestContent: { parsed, header },
|
errorMessage: String(err),
|
});
|
}
|
}
|
|
/**
|
* 从 ISAPI ActiveSafety 解析并写入设备告警表
|
*/
|
async processActiveSafetyFromIsapi(parsed: any, header: any) {
|
try {
|
const safety = parsed.ISAPI?.Intelligent?.ActiveSafety || parsed;
|
const eventType = safety.EventType || safety.eventType || 'UNKNOWN';
|
const gps = safety.GPS || {};
|
const snapshot = safety.Snapshot || {};
|
const eventStateRaw = safety.EventState || safety.eventState || 'Start';
|
const eventState = eventStateRaw === 'End' ? 1 : 0;
|
|
// ✅ 补 triggerTime
|
const triggerTime = safety.TriggerTime || parsed.dateTime
|
? new Date((safety.TriggerTime || parsed.dateTime).replace(' ', 'T'))
|
: new Date();
|
|
// 判断算法大类
|
const adasTypes = ['FCW', 'LDW', 'PCW', 'TSR', 'HMW', 'FCL', 'TLD', 'CLD', 'CAW'];
|
const dsmTypes = ['Fatigue', 'Distraction', 'Phone', 'Smoking', 'NoSeatbelt', 'CameraBlocked', 'FaceRecognition'];
|
let algorithmType = 'UNKNOWN';
|
if (adasTypes.includes(eventType)) algorithmType = 'ADAS';
|
else if (dsmTypes.includes(eventType)) algorithmType = 'DSM';
|
else if (eventType.toLowerCase().includes('fatigue') || eventType.toLowerCase().includes('distraction')) algorithmType = 'DSM';
|
else if (eventType.toLowerCase().includes('fcw') || eventType.toLowerCase().includes('ldw')) algorithmType = 'ADAS';
|
|
// 图片处理
|
const pictureList: Array<{ url: string; id: string }> = [];
|
if (snapshot.URL || snapshot.url) {
|
const imgUrl = snapshot.URL || snapshot.url;
|
const crypt = snapshot.Crypt || snapshot.crypt || 0;
|
if (crypt == 0) {
|
try {
|
const filePlugin = await this.pluginService.getInstance('upload');
|
const localUrl = await filePlugin.downAndUpload(imgUrl);
|
pictureList.push({ url: localUrl, id: header.messageId });
|
} catch (e) {
|
console.error('ActiveSafety image download failed:', e);
|
pictureList.push({ url: imgUrl, id: header.messageId });
|
}
|
} else {
|
pictureList.push({ url: imgUrl, id: header.messageId });
|
}
|
}
|
|
// 组装位置信息字符串
|
let locationStr = '';
|
if (gps.Longitude && gps.Latitude) {
|
locationStr = `${gps.Longitude},${gps.Latitude}`;
|
if (gps.Speed != null) locationStr += `,speed:${gps.Speed}`;
|
}
|
|
const customInfo = JSON.stringify({
|
algorithmType,
|
alarmLevel: safety.AlarmLevel || safety.alarmLevel || 0,
|
confidence: safety.Confidence || safety.confidence || 0,
|
eventState: eventStateRaw,
|
gpsSnapshot: gps,
|
rawEventType: eventType,
|
});
|
|
// ✅ alarmTime 转 string 对齐 Entity 定义
|
const alarmTimeStr = triggerTime.toISOString().slice(0, 19).replace('T', ' ');
|
|
await this.pushDevicealarmEntity.save({
|
devSerial: header.deviceId,
|
channelNo: String(safety.ChannelID || safety.channelNo || header.channelNo || ''),
|
alarmId: header.messageId,
|
alarmTime: alarmTimeStr,
|
alarmType: eventType,
|
channelName: algorithmType === 'ADAS' ? '前视通道' : '司机通道',
|
channelType: 0,
|
checksum: undefined,
|
crypt: 0,
|
customInfo,
|
customType: '',
|
describe: `${algorithmType} ${eventType} alarm`,
|
location: locationStr,
|
relationId: safety.RelationID || safety.relationId || '',
|
status: eventState,
|
pictureList: pictureList.length > 0 ? pictureList : undefined,
|
|
// 新增字段
|
algorithmType,
|
alarmLevel: Number(safety.AlarmLevel || 0),
|
confidence: Number(safety.Confidence || 0),
|
eventState,
|
longitude: Number(gps.Longitude || 0),
|
latitude: Number(gps.Latitude || 0),
|
speedAtEvent: Number(gps.Speed || 0),
|
direction: Number(gps.Direction || 0),
|
});
|
} catch (err) {
|
console.error('processActiveSafetyFromIsapi error:', err);
|
await this.writePushAuditLog('isapi_active_safety', {
|
deviceNo: header.deviceId,
|
requestContent: { parsed, header },
|
errorMessage: String(err),
|
});
|
}
|
}
|
|
/**
|
* 从 ISAPI violentDriving 解析并写入设备告警表(G-Sensor)
|
*/
|
async processGsensorFromIsapi(parsed: any, header: any) {
|
try {
|
const driving = parsed.ISAPI?.ViolentDriving || parsed;
|
const gps = driving.GPS || {};
|
const eventStateRaw = driving.EventState || driving.eventState || 'Start';
|
const eventState = eventStateRaw === 'End' ? 1 : 0;
|
|
// ✅ 补 triggerTime
|
const triggerTime = driving.TriggerTime || parsed.dateTime
|
? new Date((driving.TriggerTime || parsed.dateTime).replace(' ', 'T'))
|
: new Date();
|
const alarmTimeStr = triggerTime.toISOString().slice(0, 19).replace('T', ' ');
|
|
let locationStr = '';
|
if (gps.Longitude && gps.Latitude) {
|
locationStr = `${gps.Longitude},${gps.Latitude}`;
|
}
|
|
const customInfo = JSON.stringify({
|
algorithmType: 'GSensor',
|
alarmLevel: driving.AlarmLevel || 0,
|
eventState: eventStateRaw,
|
gpsSnapshot: gps,
|
extraData: {
|
acceleration: driving.Acceleration || driving.acceleration,
|
deceleration: driving.Deceleration || driving.deceleration,
|
turnAngle: driving.TurnAngle || driving.turnAngle,
|
},
|
});
|
|
const finalAlarmType = driving.EventType || parsed.eventType || 'violentDriving';
|
|
await this.pushDevicealarmEntity.save({
|
devSerial: header.deviceId,
|
channelNo: String(header.channelNo || '0'),
|
alarmId: header.messageId,
|
alarmTime: alarmTimeStr,
|
alarmType: finalAlarmType,
|
channelName: '车载G-Sensor',
|
channelType: 0,
|
checksum: undefined,
|
crypt: 0,
|
customInfo,
|
customType: '',
|
describe: `G-Sensor ${finalAlarmType}`,
|
location: locationStr,
|
relationId: driving.RelationID || driving.relationId || '',
|
status: eventState,
|
pictureList: undefined,
|
|
// ✅ 新增字段
|
algorithmType: 'GSensor',
|
alarmLevel: Number(driving.AlarmLevel || 0),
|
confidence: 0,
|
eventState,
|
longitude: Number(gps.Longitude || 0),
|
latitude: Number(gps.Latitude || 0),
|
speedAtEvent: Number(gps.Speed || 0),
|
direction: Number(gps.Direction || 0),
|
});
|
} catch (err) {
|
console.error('processGsensorFromIsapi error:', err);
|
await this.writePushAuditLog('isapi_gsensor', {
|
deviceNo: header.deviceId,
|
requestContent: { parsed, header },
|
errorMessage: String(err),
|
});
|
}
|
}
|
|
/**
|
* 计算处理后的重量
|
* 规则:
|
* 1. 去皮重:干垃圾/可回收物 -1kg,湿垃圾 -10kg,其它类型同干垃圾(-1kg)
|
* 2. 去皮后重量 × 1/10
|
* 3. 结果不小于 0
|
* SW60 有毒有害
|
* SW61 湿垃圾
|
* SW62001 可回收-废纸张
|
* SW62002 可回收-废塑料
|
* SW62003 可回收-废金属
|
* SW62004 可回收-废玻璃
|
* SW62005 可回收-废织物
|
* SW62006 可回收-废旧电子产品
|
* SW62007 可回收-废旧电源电池
|
* SW63 大件垃圾
|
* SW64 干垃圾
|
*/
|
private calcProcessedWeight(garbageType: string, weight: number): number {
|
const TARE_MAP: Record<string, number> = { SW61: 10 };
|
const tareWeight = TARE_MAP[garbageType] ?? 1;
|
return Math.max((weight - tareWeight) / 10, 0);
|
}
|
|
}
|