关于Winnet的若干整理

本文分享了使用WinInet进行HTTP/HTTPS开发的经验,详细介绍了URLDownloadToFile、InternetOpen等函数的应用,以及解决常见问题的方法,如HttpOpenRequest的参数设置、122错误处理等。

很久之前用VC做了一个基于http/https的项目,当时把用wininet开发http/https程序的各种问题都搞得十分清楚。由于当时没有总结,以至于现在又出现相同的问题,又得重新查资料,但还好代码在那里。一些问题看看代码也就明白。

后来由于工作变迁,离开了原来的公司。那部分代码现在也没有了。所以又得从头来过。

现在又做一个http的项目,将一些问题总结一下,为以后再遇到类似的问题节省时间。

1. 几个wininet函数。

URLDownloadToFile:给一个文件的url,就可以把文件下载下来,需要差数可以只有两个,url和文件保存的路径。该函数封装了http,ftp,gppher协议的函数,只要是正确的url都可以下载。

URLDownloadToFile拆分为三个函数:InternetOpen,InternetOpenUrl和InternetReadFile,InternetOpenUrl也是一个封装好了的函数,它也不管具体的协议内容。InternetOpenUrl返回一个HINTERNET句柄,该句柄作为HttpQueryInfo的入参,可以得到相关的信息,如文件的长度,文件修改的日期等。该句柄同时作为InternetReadFile的入参,InternetReadFile将文件下载到一个申请好的缓冲区中。

如果是http协议,InternetOpenUrl可以分解为InternetConnect,HttpOpenRequest,HttpSendRequest。InternetConnect负责连接服务器,HttpOpenRequest去创建一个请求句柄并且把参数存储在句柄中。HttpSendRequest把请求参数送到HTTP服务器。

几个可能会遇到,并且比较郁闷的问题。

1. HttpOpenRequest的问题
HttpOpenRequest的时候如果代码像下面这样写,一般不会出现什么问题。

HINTERNET hRequest = HttpOpenRequest(hConnect,
    "GET",
    pszLoctionFilePath,
    HTTP_VERSION,
    NULL, 
    (const char **)p,
    0,
    1);

倒数第二个参数:IN DWORD dwFlags,msdn上说明:dwFlags Internet flag values. Can be any of the following values: ……
看上去如果是0的话,应该没有什么问题。但我的问题就出在这里。
参数为0的话,就会在第二次访问同一个url(不同的url不会有问题)的时候,HttpQueryInfo会失败, GetLastError()为12150:Header Not Found。这个时候如果打开IE选项,general->delete->delete files。就好了。我用程序删除缓冲区里的全部文件,没有用。非得手动点击一下delete files。这个问题产生的原因还没有找到,如果有谁遇到过同样的问题,麻烦告诉我一声。

倒数第三个参数,我的写法是:
char   szHead[] = "Accept: */*/r/n/r/n";
char **p = new char*[2];*p = szHead;*(p+1) = NULL;
之后倒数第三个参数为:(const char **)p,这样写不会有错。
如果写成(const char **)&szHead,程序不会报错,但debug调试的话,会有First-chance exception in HttpAndFtpTest.exe (KERNEL32.DLL): 0xC0000005: Access Violation.的警告,这是因为强制把一个1维数组变成2维数组,它的第二个数组没有'/0'结尾所导致的访问冲突。

2. 122错误

HttpOpenRequest之后报122:The data area passed to a system call is too small. 错误,原因没有找到。但程序不会有错误,也没有警告。

3. 各个过程花费时间

InternetOpen,InternetConnect,HttpOpenRequest,HttpQueryInfo基本上不花时间。HttpSendRequest和InternetReadFile,占用整个下载过程的绝大部分时间。

4. IE请求对应的程序的写法

http://www.abc.com/123/edf.asp?Key=login&login=2&password=1
对应的IE请求

// 连接服务器
HINTERNET hConnect = InternetConnect(hSession, 
    ServerName, 
    INTERNET_DEFAULT_HTTP_PORT, 
    NULL, 
    NULL, 
    INTERNET_SERVICE_HTTP, 
    0, 
    1);
ServerName为www.abc.com

// 创建一个请求
HINTERNET hRequest = HttpOpenRequest(hConnect,
    Method,
    FormAction,
    HTTP_VERSION,
    NULL, 
    (const char**)&accept,
    0, 
    1);
Method为"GET"
FormAction为/123/edf.asp
BOOL bSeccuss = HttpSendRequest( hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata))
frmdata为Key=login&login=2&password=1。

5. 删除IE缓存的函数

void   ClearInternetCache()   
{   
DWORD   dwNeeded   =   0;   
FindFirstUrlCacheEntry(NULL,   NULL,   &dwNeeded);  

if   (GetLastError()   ==   ERROR_INSUFFICIENT_BUFFER)   
{   
 unsigned   char   *buffer   =   new   unsigned   char[dwNeeded];   
 try   
 {   
  LPINTERNET_CACHE_ENTRY_INFO   lpicei   =   
  reinterpret_cast<LPINTERNET_CACHE_ENTRY_INFO>(buffer);   
  HANDLE   HFind   =     FindFirstUrlCacheEntry(NULL,   lpicei,   &dwNeeded);   
  DeleteUrlCacheEntry(lpicei->lpszSourceUrlName);  

  bool   no_more_files   =   false;   
  while   (!no_more_files)   
  {   
   if   (FindNextUrlCacheEntry(HFind,   lpicei,   &dwNeeded))   
   {   
    DeleteUrlCacheEntry(lpicei->lpszSourceUrlName);   
   }   
   else   switch   (GetLastError())   
   {   
    case   ERROR_INSUFFICIENT_BUFFER:   
    {   
     delete   []   buffer;   
     buffer   =   new   unsigned   char[dwNeeded];   
     lpicei   =   reinterpret_cast<LPINTERNET_CACHE_ENTRY_INFO>(buffer);   
     break;   
    }   
    default:   
    {   
     no_more_files   =   true;   
     break;   
    }   
   }   
  }
  FindCloseUrlCache(HFind);
 }   
 catch   (...)   
 {   
  delete   []   buffer;   
 }   
 delete   []   buffer;                   
}   
}

6. http和https的请求

InternetConnect 的第三个参数,INTERNET_DEFAULT_HTTP_PORT改为INTERNET_DEFAULT_HTTPS_PORT
HttpOpenRequest 的第七个参数 多了一个INTERNET_FLAG_SECURE 选项

还有一个自动安装证书的代码以前做过,现在找不到了。 

7. 更好的InternetOpen方法

DWORD dwFlags = 1;
InternetGetConnectedState(&dwFlags, 0);
if(!(dwFlags & INTERNET_CONNECTION_PROXY))
 *hSession = InternetOpenA("MyAgent", INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, NULL, NULL, 0);
else
 *hSession = InternetOpenA("MyAgent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (*hSession)
 return TRUE;
else
 return FALSE;

8. 下载文件的函数
UINT InternetGetFile (HINTERNET IN hConnect, // Handle from InternetOpen()
                 LPCSTR szFileName)
{
 FILE * pFile;
 if  ( !(pFile = fopen (szFileName, "wb" ) ) )
 {
  return INTERNET_ERROR_FILEOPEN;
 }
 VOID* szTemp[16384];
 DWORD dwSize;
 while (TRUE)
 {
  // Keep coping in 16 KB chunks, while file has any data left.
  // Note: bigger buffer will greatly improve performance.
  if (!InternetReadFile (hConnect, szTemp, 16384,  &dwSize) )
  {
   fclose (pFile);
   return INTERNET_ERROR_READFILE;
  }
  if (!dwSize)
   break;  // Condition of dwSize=0 indicate EOF. Stop.
  else
   fwrite(szTemp, sizeof (char), dwSize , pFile);
 }   // do
 fflush (pFile);
 fclose (pFile);
 return 0;
}

9. 设置internet session

如果要设置internet session及其他参数,可以调用InternetSetOption。 

 

 

转载地址:http://blog.youkuaiyun.com/huzy204/archive/2007/12/12/1932109.aspx

import numpy as np import matplotlib.pyplot as plt import pandas as pd from scipy.spatial.distance import euclidean from shapely.geometry import Polygon, Point, LineString import random from datetime import datetime import os # 字体设置 plt.rcParams['font.sans-serif'] = ['SimHei'] # 确保中文显示 plt.rcParams['font.size'] = 12 plt.rcParams['font.weight'] = 'heavy' # 船舶参数设置 SHIP_SPEEDS = [20, 24, 26, 30] # 节 SHIP_START_POINTS = [(187, 175), (169, 112), (50, 88), (73, 214)] # 起始点 # 任务清单参数 TASK_LOCATIONS = [(53, 119), (207, 143), (35, 100), (120, 110), (80, 190)] TASK_TYPES = ['S1', 'S2', 'S3', 'S4', 'S4'] TASK_DURATIONS = [2, 0, 0, 2, 4] # 小时 TASK_TRACKING_DIST = [0, 3, 1, 0, 0] # 海里 MOVING_TASKS = [False, True, True, False, False] MOVING_SPEEDS = [0, 15, 15, 0, 0] # 节 MOVING_COURSES = [0, 309, 86.08, 0, 0] # 度 # 任务区线 EXIT_POLYGON = Polygon([(194.0, 177.6), (173.4, 106.9), (42.5, 81.2), (68.1, 220.7), (144.6, 211.5)]) # 巡航线:5个点围成的闭合线圈 Cruise_line = [(187.05, 175.1), (168.65, 112.1), (50, 88.75), (73, 214.05), (142.45, 205.75)] cruise_polygon = Polygon(Cruise_line) cruise_boundary = LineString(cruise_polygon.exterior.coords) # 闭合边界线 # 遗传算法参数设置 POP_SIZE = 4 MAX_GEN = 50 CX_PROB = 0.85 MUT_PROB = 0.15 ELITE_RATIO = 0.1 KNOTS_TO_KMH = 1 NAUTICAL_TO_KM = 1 # == == == == == 辅助函数(无修改) == == == == == def calc_course(from_pos, to_pos): """计算两点航向(0°正北,顺时针递增)""" dx = to_pos[0] - from_pos[0] dy = to_pos[1] - from_pos[1] if dx == 0 and dy == 0: return 0.0 math_angle = np.arctan2(dy, dx) course = (90 - np.degrees(math_angle)) % 360 return round(course, 6) def get_closest_cruise_point(end_pos): """计算任务结束点到巡航线边界的最短距离点""" end_point = Point(end_pos) proj_dist = cruise_boundary.project(end_point) closest_point = cruise_boundary.interpolate(proj_dist) closest_pos = (round(closest_point.x, 6), round(closest_point.y, 6)) boundary_vertices = list(cruise_boundary.coords)[:-1] min_x, max_x = min(v[0] for v in boundary_vertices), max(v[0] for v in boundary_vertices) min_y, max_y = min(v[1] for v in boundary_vertices), max(v[1] for v in boundary_vertices) cx, cy = closest_pos if not (min_x - 1e-6 <= cx <= max_x + 1e-6 and min_y - 1e-6 <= cy <= max_y + 1e-6): vertex_dists = [euclidean(end_pos, v) for v in boundary_vertices] closest_idx = np.argmin(vertex_dists) closest_pos = boundary_vertices[closest_idx] return closest_pos def calc_end_course_to_cruise(end_pos): """计算指向巡航线的航向""" closest_pos = get_closest_cruise_point(end_pos) return calc_course(end_pos, closest_pos) def get_target_position(task_id, t): """计算任务在t时刻的位置(移动任务动态计算)""" if not MOVING_TASKS[task_id]: return TASK_LOCATIONS[task_id] x0, y0 = TASK_LOCATIONS[task_id] speed_kmh = MOVING_SPEEDS[task_id] * KNOTS_TO_KMH course_rad = np.deg2rad(MOVING_COURSES[task_id]) dx = speed_kmh * t * np.sin(course_rad) dy = speed_kmh * t * np.cos(course_rad) return (round(x0 + dx, 6), round(y0 + dy, 6)) def is_target_exited(task_id, t): """判断移动任务是否超出任务区""" if not MOVING_TASKS[task_id]: return False pos = get_target_position(task_id, t) return not EXIT_POLYGON.contains(Point(pos)) def calc_distance(pos1, pos2): """计算两点距离""" return round(euclidean(pos1, pos2), 6) def calc_travel_time(ship_speed, distance): """计算航行时间""" if distance == 0: return 0.0 speed_kmh = ship_speed * KNOTS_TO_KMH return round(distance / speed_kmh, 6) def calc_exit_time(start_pos, course_deg, speed, polygon): """计算目标从start_pos沿course_deg方向以速度speed移动到polygon边界所需时间(小时)""" # 如果起点已经在多边形外,则时间为0 start_point = Point(start_pos) if not polygon.contains(start_point): return 0.0 # 将方向角转换为弧度,并计算方向向量(数学坐标系:0度正东,逆时针) angle_rad = np.deg2rad(90 - course_deg) direction = (np.sin(angle_rad), np.cos(angle_rad)) # (dx, dy)单位向量 # 创建一个长的射线(10000公里) ray_length = 10000 end_pos = (start_pos[0] + direction[0] * ray_length, start_pos[1] + direction[1] * ray_length) ray_line = LineString([start_pos, end_pos]) # 求射线与多边形的交点 if ray_line.intersects(polygon.boundary): intersection = ray_line.intersection(polygon.boundary) if intersection.geom_type == 'MultiPoint': # 取离起点最近的点 points = list(intersection.geoms) min_dist = float('inf') closest_point = None for p in points: dist = Point(start_pos).distance(p) if dist < min_dist: min_dist = dist closest_point = p exit_point = closest_point elif intersection.geom_type == 'Point': exit_point = intersection else: # 可能是LineString,取第一个点(理论上不会出现) exit_point = intersection.coords[0] exit_point = Point(exit_point) # 计算起点到交点的距离 exit_dist = Point(start_pos).distance(exit_point) return exit_dist / speed # 速度单位是节,假设1节=1公里/小时 else: # 没有交点,说明方向错误?返回0 return 0.0 # == == == == == 遗传算法核心(无修改) == == == == == class GAOptimizer: def __init__(self, num_ships=4, num_tasks=5): self.num_ships = num_ships self.num_tasks = num_tasks self.forced_tasks = [0, 1, 2] # 强制出发时刻为0的任务 self.normal_tasks = [3, 4] # 普通任务 def init_population(self): """初始化种群(无修改)""" population = [] for _ in range(POP_SIZE): forced_shuffled = random.sample(self.forced_tasks, len(self.forced_tasks)) normal_shuffled = random.sample(self.normal_tasks, len(self.normal_tasks)) chrom = forced_shuffled + normal_shuffled population.append(chrom) return population def evaluate_individual(self, individual): """评估个体适应度 - 修正S2/S3任务处理逻辑""" ship_times = [0.0] * self.num_ships ship_positions = [pos for pos in SHIP_START_POINTS] used_ships_for_forced = set() for task_id in individual: if task_id in self.forced_tasks: for ship_idx in range(self.num_ships): if ship_idx not in used_ships_for_forced: target_ship_idx = ship_idx used_ships_for_forced.add(ship_idx) break current_time = 0.0 else: target_ship_idx = np.argmin(ship_times) current_time = ship_times[target_ship_idx] ship_speed = SHIP_SPEEDS[target_ship_idx] current_pos = ship_positions[target_ship_idx] if MOVING_TASKS[task_id]: target_pos = get_target_position(task_id, current_time) else: target_pos = TASK_LOCATIONS[task_id] distance = calc_distance(current_pos, target_pos) travel_time = calc_travel_time(ship_speed, distance) if TASK_TYPES[task_id] in ['S2', 'S3']: # 修正:S2/S3任务需要跟踪目标移动一段距离 # 计算舰艇到达目标位置的时间(考虑目标移动) # 计算目标从target_pos_after_travel位置移动到任务区边界的时间 # 目标在航行时间后的位置 travel_time = calc_travel_time( ship_speed, calc_distance(current_pos, TASK_LOCATIONS[task_id]) ) # 修正:计算追踪时间(目标移动指定距离所需时间) tracking_time = TASK_TRACKING_DIST[task_id] / MOVING_SPEEDS[task_id] target_pos_after_travel = get_target_position(task_id, travel_time) exit_time = calc_exit_time( target_pos_after_travel, MOVING_COURSES[task_id], MOVING_SPEEDS[task_id], EXIT_POLYGON ) # 修正:总任务时间 = 航行时间 + 追踪时间 task_time = travel_time + tracking_time + exit_time # 修正:任务结束位置应为目标移动后的位置 end_pos = get_target_position(task_id, current_time + travel_time + tracking_time + exit_time) ship_times[target_ship_idx] = current_time + task_time ship_positions[target_ship_idx] = end_pos else: task_time = travel_time + TASK_DURATIONS[task_id] ship_times[target_ship_idx] = current_time + task_time ship_positions[target_ship_idx] = get_target_position(task_id, ship_times[target_ship_idx]) total_time = max(ship_times) return 1 / (total_time + 1e-6), total_time def evaluate_population(self, population): """评估种群(无修改)""" fitness_list = [] total_time_list = [] for ind in population: fit, total_time = self.evaluate_individual(ind) fitness_list.append(fit) total_time_list.append(total_time) return fitness_list, total_time_list def select_parents(self, population, fitness): """选择父代(无修改)""" parents = [] for _ in range(POP_SIZE): candidates = random.sample(range(POP_SIZE), 3) winner = max(candidates, key=lambda i: fitness[i]) parents.append(population[winner]) return parents def crossover(self, parent1, parent2): """交叉操作(无修改)""" size = len(parent1) forced_size = len(self.forced_tasks) start_forced, end_forced = sorted(random.sample(range(forced_size), 2)) start_normal = forced_size end_normal = size - 1 start_normal_rand, end_normal_rand = sorted(random.sample(range(start_normal, end_normal + 1), 2)) child1 = [-1] * size child1[start_forced:end_forced + 1] = parent1[start_forced:end_forced + 1] child1[start_normal_rand:end_normal_rand + 1] = parent1[start_normal_rand:end_normal_rand + 1] idx = 0 for gene in parent2[:forced_size]: if gene not in child1[:forced_size] and idx < forced_size: while child1[idx] != -1: idx += 1 child1[idx] = gene idx = start_normal for gene in parent2[start_normal:]: if gene not in child1[start_normal:] and idx <= end_normal: while child1[idx] != -1: idx += 1 child1[idx] = gene child2 = [-1] * size child2[start_forced:end_forced + 1] = parent2[start_forced:end_forced + 1] child2[start_normal_rand:end_normal_rand + 1] = parent2[start_normal_rand:end_normal_rand + 1] idx = 0 for gene in parent1[:forced_size]: if gene not in child2[:forced_size] and idx < forced_size: while child2[idx] != -1: idx += 1 child2[idx] = gene idx = start_normal for gene in parent1[start_normal:]: if gene not in child2[start_normal:] and idx <= end_normal: while child2[idx] != -1: idx += 1 child2[idx] = gene return child1, child2 def mutate(self, individual): """变异操作(无修改)""" forced_size = len(self.forced_tasks) if random.random() < 0.5: idx1, idx2 = random.sample(range(forced_size), 2) else: idx1, idx2 = random.sample(range(forced_size, len(individual)), 2) individual[idx1], individual[idx2] = individual[idx2], individual[idx1] return individual def optimize(self): """优化主逻辑(无修改)""" population = self.init_population() best_fitness_history = [] avg_fitness_history = [] current_gen_best_time_history = [] current_gen_avg_time_history = [] best_solution = None best_fitness = 0 best_total_time = float('inf') for gen in range(MAX_GEN): fitness_list, total_time_list = self.evaluate_population(population) current_best_fit = max(fitness_list) current_avg_fit = sum(fitness_list) / POP_SIZE current_best_time = min(total_time_list) current_avg_time = sum(total_time_list) / POP_SIZE best_fitness_history.append(current_best_fit) avg_fitness_history.append(current_avg_fit) current_gen_best_time_history.append(current_best_time) current_gen_avg_time_history.append(current_avg_time) if current_best_fit > best_fitness: best_fitness = current_best_fit best_total_time = current_best_time best_solution = population[np.argmax(fitness_list)].copy() parents = self.select_parents(population, fitness_list) offspring = [] for i in range(0, POP_SIZE, 2): if i + 1 < POP_SIZE: p1, p2 = parents[i], parents[i + 1] if random.random() < CX_PROB: c1, c2 = self.crossover(p1, p2) else: c1, c2 = p1.copy(), p2.copy() offspring.append(c1) offspring.append(c2) offspring = [self.mutate(ind) for ind in offspring] elite_size = int(ELITE_RATIO * POP_SIZE) elite_indices = np.argsort(fitness_list)[-elite_size:] for i in range(elite_size): offspring[i] = population[elite_indices[i]].copy() population = offspring if gen % 10 == 0: print(f"Generation {gen}: " f"Best Time = {current_best_time:.2f}h, " f"Avg Time = {current_avg_time:.2f}h, " f"Best Fitness = {current_best_fit:.4f}") return (best_solution, best_fitness, best_total_time, best_fitness_history, avg_fitness_history, current_gen_best_time_history, current_gen_avg_time_history) def decode_solution(self, solution): """任务解码(无修改,保持原有航向逻辑)""" ship_times = [0.0] * self.num_ships # 舰艇当前可用时间 ship_positions = [pos for pos in SHIP_START_POINTS] # 舰艇当前位置 ship_tasks = [[] for _ in range(self.num_ships)] # 存储各舰艇的任务详情 used_ships_for_forced = set() # 已分配强制任务的舰艇 # 第一步:先将所有任务分配到对应舰艇,暂不计算“任务结束后航向” for task_id in solution: # 1. 确定任务分配的舰艇 if task_id in self.forced_tasks: # 强制任务:分配给未使用的舰艇 for ship_idx in range(self.num_ships): if ship_idx not in used_ships_for_forced: target_ship_idx = ship_idx used_ships_for_forced.add(ship_idx) break depart_time = 0.0 # 强制任务0时刻出发 else: # 普通任务:分配给最早空闲的舰艇 target_ship_idx = np.argmin(ship_times) depart_time = ship_times[target_ship_idx] # 2. 计算当前任务的核心参数(出发/开始/结束信息) ship_speed = SHIP_SPEEDS[target_ship_idx] depart_pos = ship_positions[target_ship_idx] # 任务起始位置(移动任务需按出发时间计算) task_start_pos = get_target_position(task_id, depart_time) # 航行时间与任务开始时间 travel_dist = calc_distance(depart_pos, task_start_pos) travel_time = calc_travel_time(ship_speed, travel_dist) task_start_time = depart_time + travel_time # 出发航向(指向当前任务的起始位置) depart_course = calc_course(depart_pos, task_start_pos) # 任务结束时间与结束位置 if TASK_TYPES[task_id] in ['S2', 'S3']: # 修正:S2/S3任务需要追踪目标移动 # 计算航行时间 travel_dist = calc_distance(depart_pos, TASK_LOCATIONS[task_id]) travel_time = calc_travel_time(ship_speed, travel_dist) # 修正:计算追踪时间(目标移动指定距离所需时间) tracking_time = TASK_TRACKING_DIST[task_id] / MOVING_SPEEDS[task_id] target_pos_after_travel = get_target_position(task_id, travel_time) exit_time = calc_exit_time( target_pos_after_travel, MOVING_COURSES[task_id], MOVING_SPEEDS[task_id], EXIT_POLYGON ) # 修正:任务开始时间 = 出发时间 + 航行时间 task_start_time = depart_time + travel_time # 修正:任务结束时间 = 开始时间 + 追踪时间 task_end_time = task_start_time + tracking_time + exit_time # 修正:任务结束位置 = 目标在结束时刻的位置 task_end_pos = get_target_position(task_id, task_end_time) # 修正:任务总耗时 = 航行时间 + 追踪时间 task_duration = travel_time + tracking_time + exit_time else: # 普通任务:固定时长 task_duration = TASK_DURATIONS[task_id] task_end_time = task_start_time + task_duration task_end_pos = get_target_position(task_id, task_end_time) # 3. 生成任务详情(“任务结束后航向”暂设为None,后续统一计算) task_detail = { "舰艇编号": target_ship_idx + 1, "任务编号": task_id, "任务类型": TASK_TYPES[task_id], "舰艇速度(节)": ship_speed, "舰艇出发时刻(小时)": round(depart_time, 6), "舰艇出发坐标(x,y)": depart_pos, "舰艇出发航向(°)": depart_course, "处置任务开始时刻(小时)": round(task_start_time, 6), "处置任务开始坐标(x,y)": task_start_pos, "处置任务结束时刻(小时)": round(task_end_time, 6), "处置任务结束坐标(x,y)": task_end_pos, "任务结束后航向(°)": None, # 占位符,后续计算 "任务耗时(小时)": round(task_duration, 6) } # 将任务详情添加到对应舰艇的任务列表 ship_tasks[target_ship_idx].append(task_detail) # 4. 更新舰艇的当前状态(为下一个任务做准备) ship_times[target_ship_idx] = task_end_time ship_positions[target_ship_idx] = task_end_pos # 第二步:遍历每个舰艇的任务列表,计算“任务结束后航向” for ship_idx in range(self.num_ships): tasks = ship_tasks[ship_idx] num_tasks = len(tasks) # 逐个处理舰艇的每个任务 for i in range(num_tasks): current_task = tasks[i] current_end_pos = current_task["处置任务结束坐标(x,y)"] if i == num_tasks - 1: # 情况1:当前任务是舰艇的最后一个任务→航向指向巡航线 end_course = calc_end_course_to_cruise(current_end_pos) else: # 情况2:当前任务有后续任务→航向指向下一个任务的起始位置 next_task = tasks[i + 1] # 下一个任务的详情 next_task_id = next_task["任务编号"] # 下一个任务的ID # 下一个任务的出发时间 = 当前任务的结束时间(舰艇完成即出发) next_depart_time = current_task["处置任务结束时刻(小时)"] # 下一个任务的起始位置(按出发时间计算) next_task_start_pos = get_target_position(next_task_id, next_depart_time) # 计算当前任务结束后,指向下一步任务的航向 end_course = calc_course(current_end_pos, next_task_start_pos) # 更新当前任务的“任务结束后航向” current_task["任务结束后航向(°)"] = end_course return ship_tasks # == == == == == 主程序(核心修改:可视化图表标注) == == == == == if __name__ == "__main__": optimizer = GAOptimizer(num_ships=4, num_tasks=5) (best_sol, best_fit, best_total_time, best_fit_hist, avg_fit_hist, gen_best_time_hist, gen_avg_time_hist) = optimizer.optimize() # 输出结果 print("\n" + "=" * 60) print("优化完成!基础结果如下:") print(f"最佳任务序列: {best_sol}") print(f"历史最佳总完成时间: {best_total_time:.2f} 小时") print(f"历史最佳适应度: {best_fit:.4f}") print(f"巡航线闭合线圈顶点: {Cruise_line}") print("=" * 60) # 导出Excel ship_tasks = optimizer.decode_solution(best_sol) all_tasks = [] for ship in ship_tasks: all_tasks.extend(ship) df = pd.DataFrame(all_tasks) column_order = [ "舰艇编号", "任务编号", "任务类型", "舰艇速度(节)", "舰艇出发时刻(小时)", "舰艇出发坐标(x,y)", "舰艇出发航向(°)", "处置任务开始时刻(小时)", "处置任务开始坐标(x,y)", "处置任务结束时刻(小时)", "处置任务结束坐标(x,y)", "任务结束后航向(°)", "任务耗时(小时)" ] df = df[column_order] timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") excel_filename = f"第一问舰艇任务分配详情_{timestamp}.xlsx" try: df.to_excel(excel_filename, index=False, engine="openpyxl") print(f"\nExcel表格已生成:{excel_filename}") print(f"文件路径:{os.path.abspath(excel_filename)}") print("提示:“任务结束后航向(°)”已按规则计算(有后续任务指向下一任务,无则指向巡航线)") except Exception as e: print(f"\n导出Excel失败:{str(e)}") print("请确保已安装依赖库:pip install pandas openpyxl shapely") # 1. 适应度变化图(无修改) plt.figure(figsize=(12, 8)) plt.plot(best_fit_hist, label="最佳适应度", color="#1f77b4", linewidth=2.5) plt.plot(avg_fit_hist, label="平均适应度", color="#ff7f0e", linewidth=2.5) plt.xlabel("迭代次数", fontsize=20) plt.ylabel("适应度(总完成时间倒数)", fontsize=20) plt.title("遗传算法迭代过程中适应度变化", fontsize=24, pad=20) plt.legend(fontsize=18, loc="upper right") plt.grid(alpha=0.3, linestyle="--") plt.xticks(fontsize=16) plt.yticks(fontsize=16) plt.tight_layout() plt.show() # 2. 总完成时间图(无修改) plt.figure(figsize=(12, 8)) plt.plot(gen_best_time_hist, label="每代最佳总完成时间", color="#2ca02c", linewidth=2.5) plt.plot(gen_avg_time_hist, label="每代平均总完成时间", color="#d62728", linewidth=2.5, linestyle="--") plt.axhline(y=best_total_time, color="#2ca02c", linestyle=":", linewidth=2, label=f"历史最佳总时间:{best_total_time:.2f}h") plt.xlabel("迭代次数", fontsize=20) plt.ylabel("总完成时间(小时)", fontsize=20) plt.title("遗传算法每代任务总完成时间变化", fontsize=24, pad=20) plt.legend(fontsize=18, loc="upper right") plt.grid(alpha=0.3, linestyle="--") plt.xticks(fontsize=16) plt.yticks(fontsize=16) last_gen = MAX_GEN - 1 plt.annotate( f'历史最佳:{best_total_time:.2f}h', xy=(last_gen, best_total_time), xytext=(last_gen - 80, best_total_time + 2), fontsize=16, arrowprops=dict(arrowstyle="->", color="#2ca02c", lw=2) ) plt.tight_layout() plt.show() # -------------------------- 3. 可视化图表(核心修改:新增编号标注) -------------------------- plt.figure(figsize=(12, 10)) # 绘制巡航线闭合线圈(无修改) cruise_x = [p[0] for p in Cruise_line] + [Cruise_line[0][0]] cruise_y = [p[1] for p in Cruise_line] + [Cruise_line[0][1]] plt.plot(cruise_x, cruise_y, color="#1f77b4", linewidth=3, label="巡航线闭合线圈") plt.fill(cruise_x, cruise_y, color="#1f77b4", alpha=0.1) # 绘制所有任务结束点、航向及编号标注(核心修改) for task in all_tasks: end_x, end_y = task["处置任务结束坐标(x,y)"] end_course = task["任务结束后航向(°)"] ship_num = task["舰艇编号"] # 舰艇编号 task_id = task["任务编号"] # 任务编号 # 1. 绘制任务结束点(红色圆点) plt.scatter(end_x, end_y, color="#d62728", s=150, zorder=5) # 2. 绘制航向箭头(无修改) arrow_len = 15 # 箭头长度(可调整) angle_rad = np.deg2rad(90 - end_course) # 航向转数学角度 dx = arrow_len * np.cos(angle_rad) dy = arrow_len * np.sin(angle_rad) plt.arrow(end_x, end_y, dx, dy, color="#d62728", width=0.8, head_width=3, head_length=3, zorder=6) # 3. 标注舰艇编号(蓝色,16号字体,位于点上方,避免遮挡) plt.text( x=end_x, y=end_y + 8, # 位置:点上方8个单位 s=f"舰艇{ship_num}", color="#d62728", # 舰艇编号用蓝色(与巡航线同色,区分度高) fontsize=17, # 舰艇编号字体更大 fontweight="bold", ha="center", va="center", zorder=7 # 层级高于点和箭头,避免被覆盖 ) # 4. 标注任务编号(黑色,12号字体,位于点下方,与舰艇编号错开) plt.text( x=end_x, y=end_y - 8, # 位置:点下方8个单位 s=f"任务{task_id}", color="black", # 任务编号用黑色 fontsize=14, # 任务编号字体更小 fontweight="normal", ha="center", va="center", zorder=7 ) # 5. 标注航向值(红色,14号字体,位于箭头延长线,避免与编号重叠) plt.text( x=end_x + dx + 3, y=end_y + dy + 3, # 位置:箭头末端外侧3个单位 s=f"{end_course}°", color="#483D8B", fontsize=18, ha="center", va="center", zorder=7 ) # 图表设置(调整标题,明确标注信息) plt.xlabel("X坐标", fontsize=20) plt.ylabel("Y坐标", fontsize=20) plt.title("任务结束后航向可视化(标注:舰艇编号/任务编号/航向)", fontsize=24, pad=20) plt.legend(fontsize=16, loc="upper left") plt.grid(alpha=0.3, linestyle="--") plt.axis("equal") # 等比例显示,避免方向失真 plt.tight_layout() plt.show()纠正上述代码
最新发布
10-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值