Guest User

Untitled

a guest
Dec 16th, 2017
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.24 KB | None | 0 0
  1. #include <iostream>
  2. #include <exception>
  3. #include <string>
  4. #include <sstream>
  5. #include <vector>
  6. #include <regex>
  7. #include <thread>
  8. #include <future>
  9. #include <cassert>
  10.  
  11. using namespace std;
  12.  
  13. enum EMovingDirection {
  14. Stop = 1,
  15. Down = 2,
  16. Up = 3
  17. };
  18.  
  19. string DirectionToStr(EMovingDirection direction) {
  20. if (direction == Stop)
  21. return "nowhere";
  22. else if (direction == Down)
  23. return "down";
  24. else if (direction == Up)
  25. return "up";
  26. else
  27. return "unknown";
  28. }
  29.  
  30. struct TLiftStatus {
  31. int Floor;
  32. EMovingDirection Direction;
  33. };
  34.  
  35. struct TLiftSettings {
  36. int MinFloor;
  37. int MaxFloor;
  38. int FloorSpeedMs;
  39. };
  40.  
  41. typedef std::chrono::system_clock Time;
  42. typedef std::chrono::milliseconds ms;
  43. typedef std::chrono::duration<float> fsec;
  44.  
  45. class ILift {
  46. public:
  47. virtual bool IsValidFloor(int floor) const = 0;
  48. virtual TLiftStatus GetStatus() const = 0;
  49. virtual TLiftSettings GetSettings() const = 0;
  50. virtual void ProcessState() = 0;
  51. virtual bool GoToFloor(int floor) = 0;
  52. };
  53.  
  54. class TLift: public ILift {
  55. private:
  56. const int MinFloor = 0;
  57. const int MaxFloor = 0;
  58. const int FloorSpeedMs = 0;
  59. const int NowhereFloor = -1;
  60. // State must be correct alltimes
  61. chrono::system_clock::time_point FloorTime = chrono::system_clock::time_point::min();
  62. int Floor = MinFloor;
  63. int GoingToFloor = NowhereFloor - 1;
  64. EMovingDirection Direction = Stop;
  65.  
  66. private:
  67. void UpdateSpeedAndPosition() {
  68. while (true) {
  69. EMovingDirection oldDirection = Direction;
  70. if (!IsValidFloor(GoingToFloor))
  71. Direction = Stop;
  72. else if (GoingToFloor > Floor)
  73. Direction = Up;
  74. else if (GoingToFloor < Floor)
  75. Direction = Down;
  76. else {
  77. GoingToFloor = NowhereFloor;
  78. Direction = Stop;
  79. }
  80. chrono::system_clock::time_point now = chrono::system_clock::now();
  81. if (Direction != oldDirection)
  82. FloorTime = now;
  83. // Not 100% correct model of a real lift
  84. if (Direction == Stop) {
  85. FloorTime = chrono::system_clock::time_point::min();
  86. break;
  87. }
  88. assert(IsValidFloor(GoingToFloor));
  89. chrono::milliseconds elapsed = chrono::duration_cast<chrono::milliseconds>(now - FloorTime);
  90. if (elapsed.count() < FloorSpeedMs)
  91. break;
  92. if (Direction == Up)
  93. Floor++;
  94. if (Direction == Down)
  95. Floor--;
  96. FloorTime += chrono::milliseconds(FloorSpeedMs);
  97. }
  98. }
  99.  
  100. public:
  101. virtual bool IsValidFloor(int floor) const {
  102. return (floor >= MinFloor && floor <= MaxFloor);
  103. }
  104. TLift(int minFloor, int maxFloor, int floorSpeedMs)
  105. : MinFloor(minFloor)
  106. , MaxFloor(maxFloor)
  107. , FloorSpeedMs(floorSpeedMs)
  108. , NowhereFloor(minFloor - 1)
  109. , Floor(MinFloor)
  110. , GoingToFloor(NowhereFloor)
  111. , Direction(EMovingDirection::Stop)
  112. {
  113. if (maxFloor <= minFloor)
  114. throw invalid_argument("TLift:: bad min/max floor parameters");
  115. }
  116. public:
  117. virtual TLiftSettings GetSettings() const {
  118. return { MinFloor, MaxFloor, FloorSpeedMs };
  119. }
  120. virtual TLiftStatus GetStatus() const {
  121. return { Floor, Direction };
  122. }
  123. virtual void ProcessState() {
  124. UpdateSpeedAndPosition();
  125. }
  126. virtual bool GoToFloor(int floor) {
  127. if (Direction != EMovingDirection::Stop)
  128. return false;
  129. if (!IsValidFloor(floor))
  130. return false;
  131. if (floor == Floor)
  132. return false;
  133. GoingToFloor = floor; // If lift is stopped then it's going anywhere yet
  134. UpdateSpeedAndPosition();
  135. return true;
  136. }
  137. };
  138.  
  139. class TLiftControllerBase {
  140. protected:
  141. ILift & Lift;
  142. TLiftSettings Settings;
  143. public:
  144. TLiftControllerBase(ILift & lift)
  145. : Lift(lift)
  146. , Settings(Lift.GetSettings())
  147. {
  148. }
  149.  
  150. bool IsValidFloor(int floor) {
  151. return (floor >= Settings.MinFloor && floor <= Settings.MaxFloor);
  152. }
  153. };
  154.  
  155. // In the future refactoring each controller type may have a custom internal
  156. // state and deliver different custom status
  157. class TInLiftController : protected TLiftControllerBase {
  158. public:
  159. TInLiftController(ILift & lift)
  160. : TLiftControllerBase(lift)
  161. {
  162. }
  163. bool PushFloorButton(int floor) {
  164. if (!IsValidFloor(floor))
  165. return false;
  166. return Lift.GoToFloor(floor);
  167. }
  168. string GetStatusStr() {
  169. TLiftStatus status = Lift.GetStatus();
  170. stringstream ss;
  171. ss << "Welcome to the lift! floor: " << status.Floor << ", going: " << DirectionToStr(status.Direction);
  172. return ss.str();
  173. }
  174. };
  175.  
  176. class TOutLiftController : protected TLiftControllerBase {
  177. private:
  178. int AtFloor = -1;
  179. public:
  180. TOutLiftController(ILift & lift, int floor)
  181. : TLiftControllerBase(lift)
  182. , AtFloor(floor)
  183. {
  184. if (!IsValidFloor(floor))
  185. throw invalid_argument("TLiftController - Wrong floor number");
  186. }
  187. public:
  188. bool PushCallButton() {
  189. return Lift.GoToFloor(AtFloor);
  190. }
  191. string GetStatusStr() {
  192. TLiftStatus status = Lift.GetStatus();
  193. stringstream ss;
  194. ss << "Push button to call the lift. floor: " << status.Floor << ", going: " << DirectionToStr(status.Direction);
  195. return ss.str();
  196. }
  197. };
  198.  
  199. class TBuilding {
  200. private:
  201. TLift Lift;
  202. TInLiftController LiftController;
  203. vector<TOutLiftController> FloorControllers; // don't modify outside constuctor, so pointers do not invalidate
  204. int controllerOffet = 0;
  205. public:
  206. TBuilding(int minFloor, int maxFloor, int speedFloorMs) // Let's construct the building ;)
  207. : Lift(minFloor, maxFloor, speedFloorMs) // Will throw exception on wrong parameters
  208. , LiftController(Lift)
  209. {
  210. controllerOffet = minFloor;
  211. for (int floor = minFloor; floor <= maxFloor; floor++) {
  212. FloorControllers.emplace_back(Lift, floor);
  213. }
  214. }
  215. TInLiftController * GetInLiftController() {
  216. return &LiftController;
  217. }
  218. TOutLiftController * GetOutLiftController(int floor) {
  219. int pos = floor - controllerOffet;
  220. if (pos < 0 || pos >= FloorControllers.size())
  221. return nullptr;
  222. return &FloorControllers[pos];
  223. }
  224. string GetStatusStr() {
  225. stringstream ss;
  226. ss << "Welcome to the building, we have floors from " << controllerOffet << " to " << controllerOffet + FloorControllers.size() << endl;
  227. ss << "You are welcome to use our lift! Ask \"help\" to get further assistance" << endl;
  228. return ss.str();
  229. }
  230. void ProcessState() {
  231. Lift.ProcessState();
  232. }
  233. };
  234.  
  235. class TLiftTextController {
  236. private:
  237. TBuilding & Building;
  238. ostream & OutStream;
  239. string LastStatus = "";
  240. TInLiftController * CurrentInLiftController = nullptr;
  241. TOutLiftController * CurrentOutLiftController = nullptr;
  242. string PanelName = "";
  243. bool ExitBuilding = false;
  244. private:
  245. void Help() {
  246. OutStream << "use lift - start using lift inner panel" << endl;
  247. OutStream << "use 1 - start using panel on the 1st floor" << endl;
  248. OutStream << "push 5 - while using lift panel push 5th floor button" << endl;
  249. OutStream << "push call - while using floor panel call the lift" << endl;
  250. OutStream << "show this help" << endl;
  251. }
  252.  
  253. public:
  254. TLiftTextController(TBuilding & building, ostream & outStream)
  255. : Building(building)
  256. , OutStream(outStream)
  257. , CurrentInLiftController(nullptr)
  258. , CurrentOutLiftController(nullptr)
  259. {
  260. OutStream << Building.GetStatusStr();
  261. }
  262.  
  263. void ProcessCommand(string command) {
  264. static regex useLiftRegex("use lift", regex_constants::icase);
  265. static regex useFloorRegex("use (-?\\d+)", regex_constants::icase);
  266. static regex pushFloorRegex = regex("push (-?\\d+)", regex_constants::icase);
  267. static regex pushCallRegex = regex("push call", regex_constants::icase);
  268. static regex helpRegex = regex("help", regex_constants::icase);
  269. static regex exitRegex = regex("exit", regex_constants::icase);
  270.  
  271. if (command.empty())
  272. return;
  273. std::smatch floor_match;
  274. if (regex_match(command, useLiftRegex)) {
  275. CurrentInLiftController = Building.GetInLiftController();
  276. CurrentOutLiftController = nullptr;
  277. PanelName = "Lift panel";
  278. OutStream << "Switched to lift panel" << endl;
  279. return;
  280. }
  281. if (regex_match(command, floor_match, useFloorRegex)) {
  282. if (floor_match.size() == 2) {
  283. int floor = stoi(floor_match[1].str());
  284. auto ctl = Building.GetOutLiftController(floor);
  285. if (ctl != nullptr) {
  286. CurrentOutLiftController = ctl;
  287. CurrentInLiftController = nullptr;
  288. OutStream << "Switched to floor panel at #" << floor << " floor" << endl;
  289. stringstream pn;
  290. pn << "Floor #" << floor << " panel";
  291. PanelName = pn.str();
  292. }
  293. else
  294. Help();
  295. }
  296. return;
  297. }
  298. if (regex_match(command, floor_match, pushFloorRegex)) {
  299. if (CurrentInLiftController != nullptr) {
  300. int floor = stoi(floor_match[1].str());
  301. bool result = CurrentInLiftController->PushFloorButton(floor);
  302. OutStream << "Pushed #" << floor << ", " << (result ? "click" : "nothing happened") << endl;
  303. }
  304. else
  305. Help();
  306. return;
  307. }
  308. if (regex_match(command, pushCallRegex)) {
  309. if (CurrentOutLiftController != nullptr) {
  310. bool result = CurrentOutLiftController->PushCallButton();
  311. OutStream << "Pushed call" << ", " << (result ? "click" : "nothing happened") << endl;
  312. }
  313. else
  314. Help();
  315. return;
  316. }
  317. if (regex_match(command, helpRegex)) {
  318. Help();
  319. return;
  320. }
  321. if (regex_match(command, exitRegex)) {
  322. ExitBuilding = true;
  323. return;
  324. }
  325. Help();
  326. }
  327.  
  328. bool UpdateStatus() {
  329. string status = "";
  330. if (CurrentInLiftController != nullptr) {
  331. status = CurrentInLiftController->GetStatusStr();
  332. } else if (CurrentOutLiftController != nullptr) {
  333. status = CurrentOutLiftController->GetStatusStr();
  334. }
  335. if (status != LastStatus) {
  336. LastStatus = status;
  337. OutStream << "[ " << PanelName << " ] " << LastStatus << endl;
  338. return true;
  339. }
  340. return false;
  341. }
  342.  
  343. void ShowPrompt() {
  344. OutStream << "> ";
  345. }
  346.  
  347. bool DoExitBuilding() const {
  348. return ExitBuilding;
  349. }
  350. };
  351.  
  352. class TAsyncLineReader {
  353. private:
  354. istream & InStream;
  355. future<string> asyncReader;
  356.  
  357. public:
  358. string ReadLineSync() {
  359. string s;
  360. getline(InStream, s);
  361. return s;
  362. }
  363.  
  364. TAsyncLineReader(istream & inStream)
  365. : InStream(inStream) {
  366.  
  367. }
  368.  
  369. void StartReading() {
  370. asyncReader = async(launch::async, &TAsyncLineReader::ReadLineSync, this);
  371. }
  372.  
  373. string ReadLine()
  374. {
  375. string result = "";
  376. if (!asyncReader.valid())
  377. StartReading();
  378. if (asyncReader.wait_for(chrono::seconds(0)) == future_status::ready)
  379. return asyncReader.get();
  380. else
  381. return "";
  382. }
  383. };
  384.  
  385. int main() {
  386. istream & inStream = cin;
  387. ostream & outStream = cerr;
  388.  
  389. TBuilding building(-2, 9, 2500);
  390. TAsyncLineReader cmdReader(inStream);
  391. TLiftTextController controller(building, outStream);
  392.  
  393. controller.ShowPrompt();
  394. cmdReader.StartReading();
  395. while (true) {
  396. string cmd = cmdReader.ReadLine();
  397. if (!cmd.empty()) {
  398. controller.ProcessCommand(cmd);
  399. if (controller.DoExitBuilding())
  400. break;
  401. controller.ShowPrompt();
  402. cmdReader.StartReading();
  403. }
  404. building.ProcessState();
  405. if (controller.UpdateStatus())
  406. controller.ShowPrompt();
  407. this_thread::sleep_for(chrono::milliseconds(100));
  408. }
  409. return 0;
  410. }
Add Comment
Please, Sign In to add comment