Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <exception>
- #include <string>
- #include <sstream>
- #include <vector>
- #include <regex>
- #include <thread>
- #include <future>
- #include <cassert>
- using namespace std;
- enum EMovingDirection {
- Stop = 1,
- Down = 2,
- Up = 3
- };
- string DirectionToStr(EMovingDirection direction) {
- if (direction == Stop)
- return "nowhere";
- else if (direction == Down)
- return "down";
- else if (direction == Up)
- return "up";
- else
- return "unknown";
- }
- struct TLiftStatus {
- int Floor;
- EMovingDirection Direction;
- };
- struct TLiftSettings {
- int MinFloor;
- int MaxFloor;
- int FloorSpeedMs;
- };
- typedef std::chrono::system_clock Time;
- typedef std::chrono::milliseconds ms;
- typedef std::chrono::duration<float> fsec;
- class ILift {
- public:
- virtual bool IsValidFloor(int floor) const = 0;
- virtual TLiftStatus GetStatus() const = 0;
- virtual TLiftSettings GetSettings() const = 0;
- virtual void ProcessState() = 0;
- virtual bool GoToFloor(int floor) = 0;
- };
- class TLift: public ILift {
- private:
- const int MinFloor = 0;
- const int MaxFloor = 0;
- const int FloorSpeedMs = 0;
- const int NowhereFloor = -1;
- // State must be correct alltimes
- chrono::system_clock::time_point FloorTime = chrono::system_clock::time_point::min();
- int Floor = MinFloor;
- int GoingToFloor = NowhereFloor - 1;
- EMovingDirection Direction = Stop;
- private:
- void UpdateSpeedAndPosition() {
- while (true) {
- EMovingDirection oldDirection = Direction;
- if (!IsValidFloor(GoingToFloor))
- Direction = Stop;
- else if (GoingToFloor > Floor)
- Direction = Up;
- else if (GoingToFloor < Floor)
- Direction = Down;
- else {
- GoingToFloor = NowhereFloor;
- Direction = Stop;
- }
- chrono::system_clock::time_point now = chrono::system_clock::now();
- if (Direction != oldDirection)
- FloorTime = now;
- // Not 100% correct model of a real lift
- if (Direction == Stop) {
- FloorTime = chrono::system_clock::time_point::min();
- break;
- }
- assert(IsValidFloor(GoingToFloor));
- chrono::milliseconds elapsed = chrono::duration_cast<chrono::milliseconds>(now - FloorTime);
- if (elapsed.count() < FloorSpeedMs)
- break;
- if (Direction == Up)
- Floor++;
- if (Direction == Down)
- Floor--;
- FloorTime += chrono::milliseconds(FloorSpeedMs);
- }
- }
- public:
- virtual bool IsValidFloor(int floor) const {
- return (floor >= MinFloor && floor <= MaxFloor);
- }
- TLift(int minFloor, int maxFloor, int floorSpeedMs)
- : MinFloor(minFloor)
- , MaxFloor(maxFloor)
- , FloorSpeedMs(floorSpeedMs)
- , NowhereFloor(minFloor - 1)
- , Floor(MinFloor)
- , GoingToFloor(NowhereFloor)
- , Direction(EMovingDirection::Stop)
- {
- if (maxFloor <= minFloor)
- throw invalid_argument("TLift:: bad min/max floor parameters");
- }
- public:
- virtual TLiftSettings GetSettings() const {
- return { MinFloor, MaxFloor, FloorSpeedMs };
- }
- virtual TLiftStatus GetStatus() const {
- return { Floor, Direction };
- }
- virtual void ProcessState() {
- UpdateSpeedAndPosition();
- }
- virtual bool GoToFloor(int floor) {
- if (Direction != EMovingDirection::Stop)
- return false;
- if (!IsValidFloor(floor))
- return false;
- if (floor == Floor)
- return false;
- GoingToFloor = floor; // If lift is stopped then it's going anywhere yet
- UpdateSpeedAndPosition();
- return true;
- }
- };
- class TLiftControllerBase {
- protected:
- ILift & Lift;
- TLiftSettings Settings;
- public:
- TLiftControllerBase(ILift & lift)
- : Lift(lift)
- , Settings(Lift.GetSettings())
- {
- }
- bool IsValidFloor(int floor) {
- return (floor >= Settings.MinFloor && floor <= Settings.MaxFloor);
- }
- };
- // In the future refactoring each controller type may have a custom internal
- // state and deliver different custom status
- class TInLiftController : protected TLiftControllerBase {
- public:
- TInLiftController(ILift & lift)
- : TLiftControllerBase(lift)
- {
- }
- bool PushFloorButton(int floor) {
- if (!IsValidFloor(floor))
- return false;
- return Lift.GoToFloor(floor);
- }
- string GetStatusStr() {
- TLiftStatus status = Lift.GetStatus();
- stringstream ss;
- ss << "Welcome to the lift! floor: " << status.Floor << ", going: " << DirectionToStr(status.Direction);
- return ss.str();
- }
- };
- class TOutLiftController : protected TLiftControllerBase {
- private:
- int AtFloor = -1;
- public:
- TOutLiftController(ILift & lift, int floor)
- : TLiftControllerBase(lift)
- , AtFloor(floor)
- {
- if (!IsValidFloor(floor))
- throw invalid_argument("TLiftController - Wrong floor number");
- }
- public:
- bool PushCallButton() {
- return Lift.GoToFloor(AtFloor);
- }
- string GetStatusStr() {
- TLiftStatus status = Lift.GetStatus();
- stringstream ss;
- ss << "Push button to call the lift. floor: " << status.Floor << ", going: " << DirectionToStr(status.Direction);
- return ss.str();
- }
- };
- class TBuilding {
- private:
- TLift Lift;
- TInLiftController LiftController;
- vector<TOutLiftController> FloorControllers; // don't modify outside constuctor, so pointers do not invalidate
- int controllerOffet = 0;
- public:
- TBuilding(int minFloor, int maxFloor, int speedFloorMs) // Let's construct the building ;)
- : Lift(minFloor, maxFloor, speedFloorMs) // Will throw exception on wrong parameters
- , LiftController(Lift)
- {
- controllerOffet = minFloor;
- for (int floor = minFloor; floor <= maxFloor; floor++) {
- FloorControllers.emplace_back(Lift, floor);
- }
- }
- TInLiftController * GetInLiftController() {
- return &LiftController;
- }
- TOutLiftController * GetOutLiftController(int floor) {
- int pos = floor - controllerOffet;
- if (pos < 0 || pos >= FloorControllers.size())
- return nullptr;
- return &FloorControllers[pos];
- }
- string GetStatusStr() {
- stringstream ss;
- ss << "Welcome to the building, we have floors from " << controllerOffet << " to " << controllerOffet + FloorControllers.size() << endl;
- ss << "You are welcome to use our lift! Ask \"help\" to get further assistance" << endl;
- return ss.str();
- }
- void ProcessState() {
- Lift.ProcessState();
- }
- };
- class TLiftTextController {
- private:
- TBuilding & Building;
- ostream & OutStream;
- string LastStatus = "";
- TInLiftController * CurrentInLiftController = nullptr;
- TOutLiftController * CurrentOutLiftController = nullptr;
- string PanelName = "";
- bool ExitBuilding = false;
- private:
- void Help() {
- OutStream << "use lift - start using lift inner panel" << endl;
- OutStream << "use 1 - start using panel on the 1st floor" << endl;
- OutStream << "push 5 - while using lift panel push 5th floor button" << endl;
- OutStream << "push call - while using floor panel call the lift" << endl;
- OutStream << "show this help" << endl;
- }
- public:
- TLiftTextController(TBuilding & building, ostream & outStream)
- : Building(building)
- , OutStream(outStream)
- , CurrentInLiftController(nullptr)
- , CurrentOutLiftController(nullptr)
- {
- OutStream << Building.GetStatusStr();
- }
- void ProcessCommand(string command) {
- static regex useLiftRegex("use lift", regex_constants::icase);
- static regex useFloorRegex("use (-?\\d+)", regex_constants::icase);
- static regex pushFloorRegex = regex("push (-?\\d+)", regex_constants::icase);
- static regex pushCallRegex = regex("push call", regex_constants::icase);
- static regex helpRegex = regex("help", regex_constants::icase);
- static regex exitRegex = regex("exit", regex_constants::icase);
- if (command.empty())
- return;
- std::smatch floor_match;
- if (regex_match(command, useLiftRegex)) {
- CurrentInLiftController = Building.GetInLiftController();
- CurrentOutLiftController = nullptr;
- PanelName = "Lift panel";
- OutStream << "Switched to lift panel" << endl;
- return;
- }
- if (regex_match(command, floor_match, useFloorRegex)) {
- if (floor_match.size() == 2) {
- int floor = stoi(floor_match[1].str());
- auto ctl = Building.GetOutLiftController(floor);
- if (ctl != nullptr) {
- CurrentOutLiftController = ctl;
- CurrentInLiftController = nullptr;
- OutStream << "Switched to floor panel at #" << floor << " floor" << endl;
- stringstream pn;
- pn << "Floor #" << floor << " panel";
- PanelName = pn.str();
- }
- else
- Help();
- }
- return;
- }
- if (regex_match(command, floor_match, pushFloorRegex)) {
- if (CurrentInLiftController != nullptr) {
- int floor = stoi(floor_match[1].str());
- bool result = CurrentInLiftController->PushFloorButton(floor);
- OutStream << "Pushed #" << floor << ", " << (result ? "click" : "nothing happened") << endl;
- }
- else
- Help();
- return;
- }
- if (regex_match(command, pushCallRegex)) {
- if (CurrentOutLiftController != nullptr) {
- bool result = CurrentOutLiftController->PushCallButton();
- OutStream << "Pushed call" << ", " << (result ? "click" : "nothing happened") << endl;
- }
- else
- Help();
- return;
- }
- if (regex_match(command, helpRegex)) {
- Help();
- return;
- }
- if (regex_match(command, exitRegex)) {
- ExitBuilding = true;
- return;
- }
- Help();
- }
- bool UpdateStatus() {
- string status = "";
- if (CurrentInLiftController != nullptr) {
- status = CurrentInLiftController->GetStatusStr();
- } else if (CurrentOutLiftController != nullptr) {
- status = CurrentOutLiftController->GetStatusStr();
- }
- if (status != LastStatus) {
- LastStatus = status;
- OutStream << "[ " << PanelName << " ] " << LastStatus << endl;
- return true;
- }
- return false;
- }
- void ShowPrompt() {
- OutStream << "> ";
- }
- bool DoExitBuilding() const {
- return ExitBuilding;
- }
- };
- class TAsyncLineReader {
- private:
- istream & InStream;
- future<string> asyncReader;
- public:
- string ReadLineSync() {
- string s;
- getline(InStream, s);
- return s;
- }
- TAsyncLineReader(istream & inStream)
- : InStream(inStream) {
- }
- void StartReading() {
- asyncReader = async(launch::async, &TAsyncLineReader::ReadLineSync, this);
- }
- string ReadLine()
- {
- string result = "";
- if (!asyncReader.valid())
- StartReading();
- if (asyncReader.wait_for(chrono::seconds(0)) == future_status::ready)
- return asyncReader.get();
- else
- return "";
- }
- };
- int main() {
- istream & inStream = cin;
- ostream & outStream = cerr;
- TBuilding building(-2, 9, 2500);
- TAsyncLineReader cmdReader(inStream);
- TLiftTextController controller(building, outStream);
- controller.ShowPrompt();
- cmdReader.StartReading();
- while (true) {
- string cmd = cmdReader.ReadLine();
- if (!cmd.empty()) {
- controller.ProcessCommand(cmd);
- if (controller.DoExitBuilding())
- break;
- controller.ShowPrompt();
- cmdReader.StartReading();
- }
- building.ProcessState();
- if (controller.UpdateStatus())
- controller.ShowPrompt();
- this_thread::sleep_for(chrono::milliseconds(100));
- }
- return 0;
- }
Add Comment
Please, Sign In to add comment