Skip to content

Commit

Permalink
feat(adapter-tcp): add more packet event handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
adrigzr committed Mar 19, 2023
1 parent 3a29e37 commit 466923c
Show file tree
Hide file tree
Showing 22 changed files with 434 additions and 17 deletions.
14 changes: 14 additions & 0 deletions packages/adapter-tcp/src/connection.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,25 @@ export class ConnectionManager {

const count = this.packetEventBus.listenerCount(event);

// Throw an error if there is no event handler for the packet event.
if (count === 0) {
throw new DomainException(`No event handler found for packet event '${event}'`);
}

// Emit the packet event.
await this.packetEventBus.emit(event, packetMessage);

// This is a hack to only mark the device as connected if there is more than one connection.
// Here we should check that the connections are from the same ip address.
if (connection.device && !connection.device.isConnected) {
const connections = this.findConnectionsByDeviceId(connection.device.id);

if (connections.length > 1) {
connection.device.setAsConnected();

await this.deviceRepository.saveOne(connection.device);
}
}
});

connection.on('close', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ export class LockDeviceWhenDeviceIsConnectedEventHandler implements DomainEventH

constructor(private readonly connectionManager: ConnectionManager) {}

handle(event: DeviceConnectedDomainEvent): Promise<void> {
async handle(event: DeviceConnectedDomainEvent): Promise<void> {
const [connection] = this.connectionManager.findConnectionsByDeviceId(event.aggregateId);

if (!connection) {
throw new DomainException(`Unable to find a connection for the device with id ${event.aggregateId.value}`);
}

return connection.send('DEVICE_CONTROL_LOCK_REQ', {});
await connection.send('DEVICE_CONTROL_LOCK_REQ', {});
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';
import type { DeviceRepository } from '@agnoc/domain';

export class ClientLoginEventHandler implements PacketEventHandler {
readonly eventName = 'CLIENT_ONLINE_REQ';

constructor(private readonly deviceRepository: DeviceRepository) {}

async handle(message: PacketMessage<'CLIENT_ONLINE_REQ'>): Promise<void> {
if (!message.device) {
const data = {
Expand All @@ -18,9 +15,5 @@ export class ClientLoginEventHandler implements PacketEventHandler {
}

await message.respond('CLIENT_ONLINE_RSP', { result: 0 });

message.device.setAsConnected();

await this.deviceRepository.saveOne(message.device);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export class DeviceBatteryUpdateEventHandler implements PacketEventHandler {

message.device.updateBattery(this.deviceBatteryMapper.toDomain(data.battery.level));

// TODO: save the entity and publish domain event

await message.respond('PUSH_DEVICE_BATTERY_INFO_RSP', { result: 0 });
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { DomainException } from '@agnoc/toolkit';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceCleanMapDataReportEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_CLEANMAP_BINDATA_REPORT_REQ';

async handle(message: PacketMessage<'DEVICE_CLEANMAP_BINDATA_REPORT_REQ'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

const data = message.packet.payload.object;

// TODO: save device clean map data

await message.respond('DEVICE_CLEANMAP_BINDATA_REPORT_RSP', { result: 0, cleanId: data.cleanId });
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { DomainException } from '@agnoc/toolkit';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceCleanMapReportEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_EVENT_REPORT_CLEANMAP';

async handle(message: PacketMessage<'DEVICE_EVENT_REPORT_CLEANMAP'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

const data = message.packet.payload.object;

// TODO: save device clean map data

await message.respond('DEVICE_EVENT_REPORT_RSP', { result: 0, body: { cleanId: data.cleanId } });
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { DomainException } from '@agnoc/toolkit';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceCleanTaskReportEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_EVENT_REPORT_CLEANTASK';

async handle(message: PacketMessage<'DEVICE_EVENT_REPORT_CLEANTASK'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

// TODO: save device clean task data

await message.respond('UNK_11A4', { unk1: 0 });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DomainException } from '@agnoc/toolkit';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceGetAllGlobalMapEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_GET_ALL_GLOBAL_MAP_INFO_RSP';

async handle(message: PacketMessage<'DEVICE_GET_ALL_GLOBAL_MAP_INFO_RSP'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

// TODO: investigate the meaning of this packet.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ export class DeviceLockedEventHandler implements PacketEventHandler {
throw new DomainException('Device not found');
}

message.device.setAsLocked();
if (!message.device.isLocked) {
message.device.setAsLocked();

await this.deviceRepository.saveOne(message.device);
await this.deviceRepository.saveOne(message.device);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MapPosition } from '@agnoc/domain';
import { DomainException } from '@agnoc/toolkit';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceMapChargerPositionUpdateEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_MAPID_PUSH_CHARGE_POSITION_INFO';

async handle(message: PacketMessage<'DEVICE_MAPID_PUSH_CHARGE_POSITION_INFO'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

const data = message.packet.payload.object;

message.device.map?.updateCharger(
new MapPosition({
x: data.poseX,
y: data.poseY,
phi: data.posePhi,
}),
);

// TODO: save entity and publish domain event
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {
DeviceCleanWork,
CleanSize,
DeviceTime,
DeviceMap,
MapCoordinate,
MapPixel,
MapPosition,
Room,
Zone,
} from '@agnoc/domain';
import { DomainException, ID, isPresent } from '@agnoc/toolkit';
import type { DeviceBatteryMapper } from '../../mappers/device-battery.mapper';
import type { DeviceErrorMapper } from '../../mappers/device-error.mapper';
import type { DeviceFanSpeedMapper } from '../../mappers/device-fan-speed.mapper';
import type { DeviceModeMapper } from '../../mappers/device-mode.mapper';
import type { DeviceStateMapper } from '../../mappers/device-state.mapper';
import type { PacketEventHandler } from '../../packet.event-handler';
import type { PacketMessage } from '../../packet.message';

export class DeviceMapUpdateEventHandler implements PacketEventHandler {
readonly eventName = 'DEVICE_MAPID_GET_GLOBAL_INFO_RSP';

constructor(
private readonly deviceBatteryMapper: DeviceBatteryMapper,
private readonly deviceModeMapper: DeviceModeMapper,
private readonly deviceStateMapper: DeviceStateMapper,
private readonly deviceErrorMapper: DeviceErrorMapper,
private readonly deviceFanSpeedMapper: DeviceFanSpeedMapper,
) {}

async handle(message: PacketMessage<'DEVICE_MAPID_GET_GLOBAL_INFO_RSP'>): Promise<void> {
if (!message.device) {
throw new DomainException('Device not found');
}

const {
statusInfo,
mapHeadInfo,
mapGrid,
historyHeadInfo,
robotPoseInfo,
robotChargeInfo,
cleanRoomList,
roomSegmentList,
wallListInfo,
spotInfo,
cleanPlanList,
currentPlanId,
} = message.packet.payload.object;

if (statusInfo) {
const {
batteryPercent: battery,
faultType: type,
workingMode: workMode,
chargeState: chargeStatus,
cleanPreference,
faultCode,
} = statusInfo;

message.device.updateCurrentClean(
new DeviceCleanWork({
size: new CleanSize(statusInfo.cleanSize),
time: DeviceTime.fromMinutes(statusInfo.cleanTime),
}),
);
message.device.updateBattery(this.deviceBatteryMapper.toDomain(battery));
message.device.updateMode(this.deviceModeMapper.toDomain(workMode));
message.device.updateState(this.deviceStateMapper.toDomain({ type, workMode, chargeStatus }));
message.device.updateError(this.deviceErrorMapper.toDomain(faultCode));
message.device.updateFanSpeed(this.deviceFanSpeedMapper.toDomain(cleanPreference));
}

let map = message.device.map;

if (mapHeadInfo && mapGrid) {
const props = {
id: new ID(mapHeadInfo.mapHeadId),
size: new MapPixel({
x: mapHeadInfo.sizeX,
y: mapHeadInfo.sizeY,
}),
min: new MapCoordinate({
x: mapHeadInfo.minX,
y: mapHeadInfo.minY,
}),
max: new MapCoordinate({
x: mapHeadInfo.maxX,
y: mapHeadInfo.maxY,
}),
resolution: mapHeadInfo.resolution,
grid: mapGrid,
rooms: [],
restrictedZones: [],
robotPath: [],
};

map = map ? map.clone(props) : new DeviceMap(props);

message.device.updateMap(map);
}

if (map) {
if (historyHeadInfo) {
const currentIndex = map.robotPath.length;

map.updateRobotPath(
map.robotPath.concat(
historyHeadInfo.pointList.slice(currentIndex).map(({ x, y }) => new MapCoordinate({ x, y })),
),
);
}

if (robotPoseInfo) {
map.updateRobot(
new MapPosition({
x: robotPoseInfo.poseX,
y: robotPoseInfo.poseY,
phi: robotPoseInfo.posePhi,
}),
);
}

if (robotChargeInfo) {
map.updateCharger(
new MapPosition({
x: robotChargeInfo.poseX,
y: robotChargeInfo.poseY,
phi: robotChargeInfo.posePhi,
}),
);
}

if (spotInfo) {
map.updateCurrentSpot(
new MapPosition({
x: spotInfo.poseX,
y: spotInfo.poseY,
phi: spotInfo.posePhi,
}),
);
}

if (wallListInfo) {
map.updateRestrictedZones(
wallListInfo.cleanAreaList.map((cleanArea) => {
return new Zone({
id: new ID(cleanArea.cleanAreaId),
coordinates: cleanArea.coordinateList.map(({ x, y }) => {
return new MapCoordinate({
x,
y,
});
}),
});
}),
);
}

if (cleanRoomList && roomSegmentList && cleanPlanList) {
const currentPlan = cleanPlanList.find((plan) => plan.planId === currentPlanId);

map.updateRooms(
cleanRoomList
.map((cleanRoom) => {
const segment = roomSegmentList.find((roomSegment) => roomSegment.roomId === cleanRoom.roomId);
const roomInfo = currentPlan?.cleanRoomInfoList.find((r) => r.roomId === cleanRoom.roomId);

if (!segment) {
return undefined;
}

return new Room({
id: new ID(cleanRoom.roomId),
name: cleanRoom.roomName,
isEnabled: Boolean(roomInfo?.enable),
center: new MapCoordinate({
x: cleanRoom.roomX,
y: cleanRoom.roomY,
}),
pixels: segment?.roomPixelList.map((pixel) => {
return new MapPixel({
x: pixel.x,
y: pixel.y,
});
}),
});
})
.filter(isPresent),
);
}
}

// TODO: save entities and publish domain events
}
}
Loading

0 comments on commit 466923c

Please sign in to comment.