/// <summary> /// Reads settings from the DB or config files. /// The files are used as a backup in case DB is not available. /// They are: log4net.config, plc.config and App.config, located in /config /// </summary> public static void readAllSettings() { Console.WriteLine("Starting AMR Connect"); // Begins logging to the console and file try { var logRepository = LogManager.GetRepository(System.Reflection.Assembly.GetEntryAssembly()); XmlConfigurator.Configure(logRepository, new FileInfo(@"config\log4net.config")); } catch (Exception exception) { Console.WriteLine("Failed To Read Logger Configuration."); Console.WriteLine(exception); } try { connectToDB(); readSettingsFromDB(); } catch (ConfigurationErrorsException exception) { logger(AREA, DEBUG, "Couldn't Read App Settings. Error: ", exception); gracefulTermination("Failed to read application settings, on start-up. App will terminate"); } //=========================================================| // Initialize Siemens PLC | //=========================================================| SiemensPLC.initialize(); //=========================================================| // Initialize SMS communication for alerts | //=========================================================| TwilioClient.Init(accountSid, authToken); //=========================================================| // Initialize PLC comms and REST Header | //=========================================================| setUpDefaultComms(); //=========================================================| // Initialize Fleet Container | //=========================================================| if (fleetManagerIP != null && fleetManagerAuthToken != null) { logger(AREA, INFO, "Fleet Connection Details Assigned"); mirFleet = new Fleet(sizeOfFleet, fleetManagerIP, fleetManagerAuthToken); } else { logger(AREA, WARNING, "Fleet Data Missing Or Fleet Not Used"); mirFleet = new Fleet(sizeOfFleet); } logger(AREA, DEBUG, "Settings Obtained"); }
/// <summary> /// /// </summary> private static int unknownMission(int robotID) { logger(AREA, ERROR, "==== Unknown Mission ===="); SiemensPLC.updateTaskStatus(robotID, Globals.TaskStatus.CouldntProcessRequest); return(Globals.TaskStatus.CouldntProcessRequest); // Issue an alert }
/// <summary> /// /// </summary> /// <returns></returns> private static void getRobotGroups() { logger(AREA, DEBUG, "Saving Robot Groups In Fleet PLC Block"); // Comment out for now - we're relying on keeping track of the groups ourselves //int restStatus = mirFleet.issueGetRequest("robots?whitelist=robot_group_id", SiemensPLC.fleetID); fleetMemoryToPLC(); SiemensPLC.writeFleetBlock(SiemensPLC.fleetBlock.getTaskStatus()); }
/// <summary> /// /// </summary> private static int clearScheduler() { logger(AREA, INFO, "==== Clear Mission Schedule ===="); int restStatus = mirFleet.fleetManager.sendRESTdata(mirFleet.fleetManager.Missions[0].deleteRequest()); SiemensPLC.updateTaskStatus(fleetID, restStatus); logger(AREA, DEBUG, "==== Cleared Mission Scheduler ===="); return(restStatus); }
/// <summary> /// Disposes default communication: HTTP Client as well as Siemens Libnodave connection. /// </summary> public static void closeComms() { logger(AREA, DEBUG, "==== Closing Connections ===="); try { comms.Dispose(); SiemensPLC.disconnect(); logger(AREA, INFO, "Closed Communications"); } catch (NullReferenceException exception) { logger(AREA, ERROR, "Couldn't close comms as they've not been instantiated: ", exception); } catch (Exception exception) { logger(AREA, ERROR, "Couldn't close comms because of the following exception: ", exception); } logger(AREA, DEBUG, "==== Finished Closing Connections ===="); }
public static void readPLCSequenceBreak() { try { string sql = "SELECT * FROM plc_sequence_reset WHERE RESET = 1;"; using var cmd = new MySqlCommand(sql, db); using MySqlDataReader rdr = cmd.ExecuteReader(); int id = 999; while (rdr.Read()) { id = rdr.GetInt32(0); SiemensPLC.updateResetBits(id); } cmd.Dispose(); rdr.Close(); if (id < 999) { using var cmd1 = new MySqlCommand("UPDATE plc_sequence_reset SET RESET = 0 WHERE ID = " + id + ";", db); issueQuery(cmd1); cmd1.Dispose(); rdr.Close(); using var cmd2 = new MySqlCommand("UPDATE plc_reset SET RESET_REQUIRED = 0", db); issueQuery(cmd2); } } catch (Exception e) { logger(AREA, ERROR, "Failed Resetting Sequence Bits"); logger(AREA, ERROR, "Exception: ", e); } }
/// <summary> /// Establishes default communications with the PLC and standard headers for HTTP REST traffic. /// </summary> public static void setUpDefaultComms() { logger(AREA, DEBUG, "==== Setting Up Connection Details ===="); try { comms = new HttpClient(); comms.DefaultRequestVersion = HttpVersion.Version11; comms.DefaultRequestHeaders.Accept.Clear(); comms.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); comms.DefaultRequestHeaders.Add("Accept-Language", "en_US"); comms.Timeout = TimeSpan.FromMinutes(10); logger(AREA, INFO, "MiR HTTP Header Created"); } catch (Exception exception) { logger(AREA, ERROR, "Failed to set up an HTTP Connection. Error: ", exception); } SiemensPLC.establishConnection(); logger(AREA, DEBUG, "==== Connections Established ===="); }
/// <summary> /// Used to check the mission status on both the Robot and the Fleet level /// </summary> private static void checkMissionAssignment(Stopwatch robotAssignment) { logger(AREA, DEBUG, "Checking Mission Assignment"); if (mirFleet.fleetManager.schedule.working_response) { // We're waiting for the Fleet to assign a robot to a mission in queue int currentMissionRobot = 0; int restStatus = 0; logger(AREA, INFO, "Current Scheduler Fleet Robot ID is: " + mirFleet.fleetManager.schedule.robot_id); logger(AREA, INFO, "Current Mission Schedule ID is: " + mirFleet.fleetManager.schedule.id); restStatus = mirFleet.issueGetRequest("mission_scheduler/" + mirFleet.fleetManager.schedule.id, fleetID); logger(AREA, DEBUG, "Mission Scheduler Robot ID After polling is: " + mirFleet.fleetManager.schedule.robot_id); if (restStatus == Globals.TaskStatus.CompletedNoErrors && mirFleet.fleetManager.schedule.robot_id != 0) { logger(AREA, INFO, "It Took Fleet " + robotAssignment.Elapsed.TotalSeconds + " Seconds To Find An Available Robot"); robotAssignment.Stop(); //currentMissionRobot = mirFleet.fleetManager.schedule.robot_id - 3; // TODO: This is as the robots are offset by one in fleet - change to be better currentMissionRobot = mirFleet.getInternalRobotID(mirFleet.fleetManager.schedule.robot_id); mirFleet.returnParameter = (short)(mirFleet.robots[currentMissionRobot].plcRobotID); logger(AREA, INFO, "Mission " + mirFleet.fleetManager.schedule.id + " Has A New Robot (Mirage: " + currentMissionRobot + ", Fleet: " + mirFleet.fleetManager.schedule.robot_id + ")"); mirFleet.robots[currentMissionRobot].schedule.id = mirFleet.fleetManager.schedule.id; occupyRobot(currentMissionRobot); restStatus = Globals.TaskStatus.CompletedNoErrors; // Add a record to the robot's job ledger - this is a new mission added on top of the existing mission stack int mission_number = mirFleet.fleetManager.schedule.mission_number; mirFleet.robots[currentMissionRobot].currentJob.startJob(mission_number, mirFleet.fleetManager.Missions[mission_number].name); mirFleet.fleetManager.schedule.working_response = false; } else { restStatus = Globals.TaskStatus.StartedProcessing; mirFleet.fleetManager.schedule.working_response = true; } SiemensPLC.writeFleetBlock(restStatus); } // Check the mission status for each robot for (int r = 0; r < sizeOfFleet; r++) { int restStatus = 0; // Don't check if we're idling or if we aborted the job // Used to be: if (mirFleet.robots[r].schedule.id != Globals.TaskStatus.Idle) // Now we don't scan if the PLC Task Status is idle (since the mir isn't doing anything) // && SiemensPLC.robots[r].getPLCTaskStatus() != Globals.TaskStatus.Idle // Also changed: // if (mirFleet.robots[r].schedule.id != Globals.TaskStatus.Idle && // To: // if (mirFleet.robots[r].schedule.id != 0 && if (mirFleet.robots[r].schedule.id != 0 && (robots[r].getPLCTaskStatus() != Globals.TaskStatus.Idle || fleetBlock.getPLCTaskStatus() != Globals.TaskStatus.Idle || mirFleet.robots[r].schedule.state_id != Globals.TaskStatus.Idle)) { // Check the status of the mission assigned to robot r logger(AREA, INFO, "==== Robot ID: " + r + " And Mission Scheduler Robot ID is: " + mirFleet.robots[r].fleetRobotID + " ===="); logger(AREA, INFO, "Mission Schedule ID is: " + mirFleet.robots[r].schedule.id); restStatus = mirFleet.checkMissionSchedule("mission_scheduler/" + mirFleet.robots[r].schedule.id + "?whilelist=state", fleetID, r); logger(AREA, INFO, "Mission Status For Robot: " + r + " is: " + mirFleet.robots[r].schedule.state); if (robots[r].getTaskParameter() == 351 || robots[r].getTaskParameter() == 352) { logger(AREA, INFO, "Conveyor On/Off Register In MiR is: " + mirFleet.robots[r].Registers[1].value); logger(AREA, INFO, "Mission Status is: " + mirFleet.robots[r].schedule.state_id); if (mirFleet.robots[r].schedule.state == "Pending") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.TaskReceivedFromPLC; } else if (mirFleet.robots[r].schedule.state == "Executing" && (int)(mirFleet.robots[r].Registers[1].value) == 1) { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.StartedProcessing; } else if (mirFleet.robots[r].schedule.state == "Outbound") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.TaskReceivedFromPLC; } else if (mirFleet.robots[r].schedule.state == "Aborted") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CouldntProcessRequest; mirFleet.robots[r].schedule.id = 0; updateTaskStatus(r, Globals.TaskStatus.CouldntProcessRequest); //mirFleet.robots[r].currentJob.finishMission(); mirFleet.robots[r].currentJob.finishJob(r, true); } else if (mirFleet.robots[r].schedule.state == "Done") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CompletedNoErrors; mirFleet.robots[r].schedule.id = 0; mirFleet.robots[r].currentJob.finishMission(); } } else { if (mirFleet.robots[r].schedule.state == "Pending") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.TaskReceivedFromPLC; } else if (mirFleet.robots[r].schedule.state == "Executing") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.StartedProcessing; } else if (mirFleet.robots[r].schedule.state == "Outbound") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.StartedProcessing; } else if (mirFleet.robots[r].schedule.state == "Aborted") { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CouldntProcessRequest; updateTaskStatus(r, Globals.TaskStatus.CouldntProcessRequest); //mirFleet.robots[r].currentJob.finishMission(); mirFleet.robots[r].currentJob.finishJob(r, true); mirFleet.robots[r].schedule.id = 0; } else if (mirFleet.robots[r].schedule.state == "Done") { // Used to be else if (mirFleet.robots[r].schedule.state == "Done") // Potentially remove this condition as the dock prox checks are now done inside MiRs if ((int)(mirFleet.robots[r].Registers[2].value) == 1) { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CompletedNoErrors; mirFleet.robots[r].schedule.id = 0; mirFleet.robots[r].currentJob.finishMission(); } else if (robots[r].getTaskParameter() == 353) { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CompletedNoErrors; mirFleet.robots[r].schedule.id = 0; mirFleet.robots[r].currentJob.finishMission(); } else { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.CouldntProcessRequest; mirFleet.robots[r].schedule.id = 0; mirFleet.robots[r].currentJob.finishMission(); } } else { mirFleet.robots[r].schedule.state_id = Globals.TaskStatus.StartedProcessing; } } logger(AREA, DEBUG, "Mission Status For Robot: " + r + " State ID is: " + mirFleet.robots[r].schedule.state_id); } } }