10.7. 使应用程序可执行
					完成红帽构建的 OptaPlanner Spring Boot timetable 项目后,将所有内容打包成一个可执行的 JAR 文件中,由标准 Java main () 方法驱动。
				
先决条件
- 您有一个已完成的 OptaPlanner Spring Boot timetable 项目。
流程
- 使用以下内容创建 - TimeTableSpringBootApp.java类:- Copy to Clipboard Copied! - Toggle word wrap Toggle overflow 
- 
							将由 Spring Initializr 创建的 src/main/java/com/example/DemoApplication.java类替换为TimeTableSpringBootApp.java类。
- 
							运行 TimeTableSpringBootApp.java类作为常规 Java 应用程序的主类。
10.7.1. 尝试 timetable 应用程序
						启动红帽构建的 OptaPlanner Spring Boot timetable 应用程序后,您可以使用您想要的任何 REST 客户端测试 REST 服务。这个示例使用 Linux curl 命令来发送 POST 请求。
					
先决条件
- OptaPlanner Spring Boot timetable 应用程序正在运行。
流程
使用以下命令:
curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
$ curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
						大约 5 秒后,终止花费 application.properties 中定义的时间,服务会返回类似以下示例的输出:
					
HTTP/1.1 200
Content-Type: application/json
...
{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}
HTTP/1.1 200
Content-Type: application/json
...
{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}请注意,应用程序将所有四个小项分配给两个时间插槽之一,以及两个空间之一。另请注意,它符合所有硬限制。例如,M. Curie 的两个定义位于不同的时间段内。
						在服务器端,info 日志显示 OptaPlanner 在 5 秒内执行的操作:
					
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4). ... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398). ... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4).
... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398).
... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).10.7.2. 构建应用程序
						良好的应用程序包括测试覆盖。这个示例测试 OptaPlanner Spring Boot 应用程序的 Timetable 红帽构建的。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController 来解决。
					
流程
							使用以下内容创建 src/test/java/com/example/solver/TimeTableControllerTest.java 类:
						
此测试会验证之后,所有较少记录都会被分配给一个时间插槽和房间。它还会验证它发现了一个可行的解决方案(没有硬约束中断)。
						通常,解析器会在 200 毫秒内找到可行的解决方案。请注意,@SpringBootTest 注释 的属性 如何覆盖 solver 终止,以便在找到可行的解决方案(0hard soft)时立即终止。这可避免硬编码一个固定时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试运行足够长,以找到可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速的系统上,它不会运行毫秒的时间超过它。
					
10.7.3. 日志记录
						完成 Red Hat build of OptaPlanner Spring Boot timetable 应用程序后,您可以使用日志信息来帮助微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制的影响。以调试模式运行应用程序,以显示应用程序采取的每个步骤,或使用 trace 日志记录来记录每个步骤和每次移动。
					
流程
- 在固定时间内运行 timetable 应用程序,例如五分钟。
- 查看日志文件中的分数计算速度,如下例所示: - ... Solving ended: ..., score calculation speed (29455/sec), ... - ... Solving ended: ..., score calculation speed (29455/sec), ...- Copy to Clipboard Copied! - Toggle word wrap Toggle overflow 
- 
								更改约束,在相同时间内再次运行 planning 应用程序,并查看日志文件中记录的分数计算速度。
- 以 debug 模式运行应用程序以记录每个步骤: - 
										要从命令行运行调试模式,请使用 -D系统属性。
- 要更改 - application.properties文件中的日志记录,请在该文件中添加以下行:- logging.level.org.optaplanner=debug - logging.level.org.optaplanner=debug- Copy to Clipboard Copied! - Toggle word wrap Toggle overflow - 以下示例以 debug 模式显示 - 日志文件中的输出:- ... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...- ... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...- Copy to Clipboard Copied! - Toggle word wrap Toggle overflow 
 
- 
										要从命令行运行调试模式,请使用 
- 
								使用 trace日志记录来显示每个步骤,以及每个步骤的每次移动。