import { call, put, select } from 'redux-saga/effects' import { goDownOneInteractionLevel } from '../actions/interaction-level' import { addIdToStoreObjectListProp, addPropToStoreObject, addToStore, removeIdFromStoreObjectListProp, } from '../actions/objects' import { cancelNewRoomConstructionSucceeded, setCurrentDatacenter, startNewRoomConstructionSucceeded, } from '../actions/topology/building' import { addRoomToDatacenter } from '../api/routes/datacenters' import { addTileToRoom, deleteRoom, updateRoom } from '../api/routes/rooms' import { addMachineToRackOnTile, addRackToTile, deleteMachineInRackOnTile, deleteRackFromTile, deleteTile, updateMachineInRackOnTile, updateRackOnTile, } from '../api/routes/tiles' import { DEFAULT_RACK_POWER_CAPACITY, DEFAULT_RACK_SLOT_CAPACITY, MAX_NUM_UNITS_PER_MACHINE, } from '../components/app/map/MapConstants' import { fetchAndStoreAllCPUs, fetchAndStoreAllGPUs, fetchAndStoreAllMemories, fetchAndStoreAllStorages, fetchAndStoreCoolingItem, fetchAndStoreCPU, fetchAndStoreDatacenter, fetchAndStoreGPU, fetchAndStoreMachinesOfTile, fetchAndStoreMemory, fetchAndStorePath, fetchAndStorePathsOfSimulation, fetchAndStorePSU, fetchAndStoreRackOnTile, fetchAndStoreRoomsOfDatacenter, fetchAndStoreSectionsOfPath, fetchAndStoreStorage, fetchAndStoreTilesOfRoom, } from './objects' export function* fetchLatestDatacenter(simulationId) { try { const paths = yield fetchAndStorePathsOfSimulation(simulationId) const latestPath = paths[paths.length - 1] const sections = yield fetchAndStoreSectionsOfPath(latestPath.id) const latestSection = sections[sections.length - 1] yield fetchAllUnitSpecifications() yield fetchDatacenter(latestSection.datacenterId) yield put(setCurrentDatacenter(latestSection.datacenterId)) } catch (error) { console.error(error) } } export function* fetchAllDatacentersOfExperiment(experiment) { try { const path = yield fetchAndStorePath(experiment.pathId) const sections = yield fetchAndStoreSectionsOfPath(path.id) path.sectionIds = sections.map(section => section.id) yield fetchAllUnitSpecifications() for (let i in sections) { yield fetchDatacenter(sections[i].datacenterId) } yield put(setCurrentDatacenter(sections[0].datacenterId)) } catch (error) { console.error(error) } } function* fetchDatacenter(datacenterId) { try { yield fetchAndStoreDatacenter(datacenterId) const rooms = yield fetchAndStoreRoomsOfDatacenter(datacenterId) yield put( addPropToStoreObject('datacenter', datacenterId, { roomIds: rooms.map(room => room.id), }), ) for (let index in rooms) { yield fetchRoom(rooms[index].id) } } catch (error) { console.error(error) } } function* fetchAllUnitSpecifications() { try { yield fetchAndStoreAllCPUs() yield fetchAndStoreAllGPUs() yield fetchAndStoreAllMemories() yield fetchAndStoreAllStorages() } catch (error) { console.error(error) } } function* fetchRoom(roomId) { const tiles = yield fetchAndStoreTilesOfRoom(roomId) yield put( addPropToStoreObject('room', roomId, { tileIds: tiles.map(tile => tile.id), }), ) for (let index in tiles) { yield fetchTile(tiles[index]) } } function* fetchTile(tile) { if (!tile.objectType) { return } switch (tile.objectType) { case 'RACK': const rack = yield fetchAndStoreRackOnTile(tile.objectId, tile.id) yield put(addPropToStoreObject('tile', tile.id, { rackId: rack.id })) yield fetchMachinesOfRack(tile.id, rack) break case 'COOLING_ITEM': const coolingItem = yield fetchAndStoreCoolingItem(tile.objectId) yield put( addPropToStoreObject('tile', tile.id, { coolingItemId: coolingItem.id }), ) break case 'PSU': const psu = yield fetchAndStorePSU(tile.objectId) yield put(addPropToStoreObject('tile', tile.id, { psuId: psu.id })) break default: console.warn('Unknown rack type encountered while fetching tile objects') } } function* fetchMachinesOfRack(tileId, rack) { const machines = yield fetchAndStoreMachinesOfTile(tileId) const machineIds = new Array(rack.capacity).fill(null) machines.forEach(machine => (machineIds[machine.position - 1] = machine.id)) yield put(addPropToStoreObject('rack', rack.id, { machineIds })) for (let index in machines) { for (let i in machines[index].cpuIds) { yield fetchAndStoreCPU(machines[index].cpuIds[i]) } for (let i in machines[index].gpuIds) { yield fetchAndStoreGPU(machines[index].gpuIds[i]) } for (let i in machines[index].memoryIds) { yield fetchAndStoreMemory(machines[index].memoryIds[i]) } for (let i in machines[index].storageIds) { yield fetchAndStoreStorage(machines[index].storageIds[i]) } } } export function* onStartNewRoomConstruction() { try { const datacenterId = yield select(state => state.currentDatacenterId) const room = yield call(addRoomToDatacenter, { id: -1, datacenterId, roomType: 'SERVER', }) const roomWithEmptyTileList = Object.assign({}, room, { tileIds: [] }) yield put(addToStore('room', roomWithEmptyTileList)) yield put( addIdToStoreObjectListProp('datacenter', datacenterId, 'roomIds', room.id), ) yield put(startNewRoomConstructionSucceeded(room.id)) } catch (error) { console.error(error) } } export function* onCancelNewRoomConstruction() { try { const datacenterId = yield select(state => state.currentDatacenterId) const roomId = yield select( state => state.construction.currentRoomInConstruction, ) yield call(deleteRoom, roomId) yield put( removeIdFromStoreObjectListProp( 'datacenter', datacenterId, 'roomIds', roomId, ), ) yield put(cancelNewRoomConstructionSucceeded()) } catch (error) { console.error(error) } } export function* onAddTile(action) { try { const roomId = yield select( state => state.construction.currentRoomInConstruction, ) const tile = yield call(addTileToRoom, { roomId, positionX: action.positionX, positionY: action.positionY, }) yield put(addToStore('tile', tile)) yield put(addIdToStoreObjectListProp('room', roomId, 'tileIds', tile.id)) } catch (error) { console.error(error) } } export function* onDeleteTile(action) { try { const roomId = yield select( state => state.construction.currentRoomInConstruction, ) yield call(deleteTile, action.tileId) yield put( removeIdFromStoreObjectListProp('room', roomId, 'tileIds', action.tileId), ) } catch (error) { console.error(error) } } export function* onEditRoomName(action) { try { const roomId = yield select(state => state.interactionLevel.roomId) const room = Object.assign( {}, yield select(state => state.objects.room[roomId]), ) room.name = action.name yield call(updateRoom, room) yield put(addPropToStoreObject('room', roomId, { name: action.name })) } catch (error) { console.error(error) } } export function* onDeleteRoom() { try { const datacenterId = yield select(state => state.currentDatacenterId) const roomId = yield select(state => state.interactionLevel.roomId) yield call(deleteRoom, roomId) yield put(goDownOneInteractionLevel()) yield put( removeIdFromStoreObjectListProp( 'datacenter', datacenterId, 'roomIds', roomId, ), ) } catch (error) { console.error(error) } } export function* onEditRackName(action) { try { const tileId = yield select(state => state.interactionLevel.tileId) const rackId = yield select( state => state.objects.tile[state.interactionLevel.tileId].objectId, ) const rack = Object.assign( {}, yield select(state => state.objects.rack[rackId]), ) rack.name = action.name yield call(updateRackOnTile, tileId, rack) yield put(addPropToStoreObject('rack', rackId, { name: action.name })) } catch (error) { console.error(error) } } export function* onDeleteRack() { try { const tileId = yield select(state => state.interactionLevel.tileId) yield call(deleteRackFromTile, tileId) yield put(goDownOneInteractionLevel()) yield put(addPropToStoreObject('tile', tileId, { objectType: undefined })) yield put(addPropToStoreObject('tile', tileId, { objectId: undefined })) } catch (error) { console.error(error) } } export function* onAddRackToTile(action) { try { const rack = yield call(addRackToTile, action.tileId, { id: -1, name: 'Rack', capacity: DEFAULT_RACK_SLOT_CAPACITY, powerCapacityW: DEFAULT_RACK_POWER_CAPACITY, }) rack.machineIds = new Array(rack.capacity).fill(null) yield put(addToStore('rack', rack)) yield put( addPropToStoreObject('tile', action.tileId, { objectId: rack.id }), ) yield put( addPropToStoreObject('tile', action.tileId, { objectType: 'RACK' }), ) } catch (error) { console.error(error) } } export function* onAddMachine(action) { try { const tileId = yield select(state => state.interactionLevel.tileId) const rackId = yield select( state => state.objects.tile[state.interactionLevel.tileId].objectId, ) const rack = yield select(state => state.objects.rack[rackId]) const machine = yield call(addMachineToRackOnTile, tileId, { id: -1, rackId, position: action.position, tags: [], cpuIds: [], gpuIds: [], memoryIds: [], storageIds: [], }) yield put(addToStore('machine', machine)) const machineIds = [...rack.machineIds] machineIds[machine.position - 1] = machine.id yield put(addPropToStoreObject('rack', rackId, { machineIds })) } catch (error) { console.error(error) } } export function* onDeleteMachine() { try { const tileId = yield select(state => state.interactionLevel.tileId) const position = yield select(state => state.interactionLevel.position) const rack = yield select( state => state.objects.rack[state.objects.tile[tileId].objectId], ) yield call(deleteMachineInRackOnTile, tileId, position) const machineIds = [...rack.machineIds] machineIds[position - 1] = null yield put(goDownOneInteractionLevel()) yield put(addPropToStoreObject('rack', rack.id, { machineIds })) } catch (error) { console.error(error) } } export function* onAddUnit(action) { try { const tileId = yield select(state => state.interactionLevel.tileId) const position = yield select(state => state.interactionLevel.position) const machine = yield select( state => state.objects.machine[ state.objects.rack[state.objects.tile[tileId].objectId].machineIds[ position - 1 ] ], ) if (machine[action.unitType + 'Ids'].length >= MAX_NUM_UNITS_PER_MACHINE) { return } const units = [...machine[action.unitType + 'Ids'], action.id] const updatedMachine = Object.assign({}, machine, { [action.unitType + 'Ids']: units, }) yield call(updateMachineInRackOnTile, tileId, position, updatedMachine) yield put( addPropToStoreObject('machine', machine.id, { [action.unitType + 'Ids']: units, }), ) } catch (error) { console.error(error) } } export function* onDeleteUnit(action) { try { const tileId = yield select(state => state.interactionLevel.tileId) const position = yield select(state => state.interactionLevel.position) const machine = yield select( state => state.objects.machine[ state.objects.rack[state.objects.tile[tileId].objectId].machineIds[ position - 1 ] ], ) const unitIds = machine[action.unitType + 'Ids'].slice() unitIds.splice(action.index, 1) const updatedMachine = Object.assign({}, machine, { [action.unitType + 'Ids']: unitIds, }) yield call(updateMachineInRackOnTile, tileId, position, updatedMachine) yield put( addPropToStoreObject('machine', machine.id, { [action.unitType + 'Ids']: unitIds, }), ) } catch (error) { console.error(error) } }