#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#pragma comment(lib, "winmm.lib")
#define _UNICODE
#define LOG_FILE L"vehicle_log.txt"
#define IDC_PAUSE_BUTTON 101 // 暂停按钮的控件 ID
// ------------------ 全局变量定义 ------------------
uint16_t current_vehicle_speed = 0; // 当前速度(km/h)
uint64_t total_distance_mm = 0; // 累计行驶距离(mm)
uint32_t last_update_time = 0; // 上次更新时间(毫秒)
const uint32_t delta_T = 20; // 每20ms记录一次日志
const uint32_t can_update_period = 100; // CAN 信号更新间隔100ms
bool is_paused = false; // 程序暂停状态
LARGE_INTEGER frequency, start_counter;
uint8_t can_frame[8] = {0}; // CAN 数据帧
// ------------------ 函数声明 ------------------
void ClearLogFile();
void WriteLog(uint32_t current_time, uint16_t speed);
void UpdateVehicleState(uint32_t current_time);
void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]);
uint16_t ReadCANMessage(uint8_t frame[8]);
DWORD WINAPI WorkerThread(LPVOID lpParam);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// ------------------ 清空日志文件 ------------------
void ClearLogFile() {
FILE* file = _wfopen(LOG_FILE, L"w");
total_distance_mm = 0; // 重置总里程
if (file) {
fwprintf(file, L"Log initialized. Vehicle Logger started.\n");
fclose(file);
}
}
// ------------------ 写入日志 ------------------
void WriteLog(uint32_t current_time, uint16_t speed) {
FILE* file = _wfopen(LOG_FILE, L"a");
if (!file) return;
SYSTEMTIME st;
GetLocalTime(&st);
int total_distance_m = (int)((total_distance_mm + 500) / 1000); // 四舍五入到米
fwprintf(file, L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %d m\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, speed, total_distance_m);
fclose(file);
}
// ------------------ 更新车辆状态 ------------------
// 修改 UpdateVehicleState 函数
void UpdateVehicleState(uint32_t current_time) {
static uint32_t last_can_update_time = 0;
static uint32_t last_log_time = 0;
static bool has_initialized = false;
static uint16_t current_speed = 0; // 静态变量,确保初始值为0
static int16_t acceleration = 0; // 加速度
const uint64_t max_distance_mm = 999999000000;
// 第一次调用时初始化所有状态
if (!has_initialized) {
current_vehicle_speed = 0; // 确保全局变量初始值为0
total_distance_mm = 0; // 重置总里程为0
last_update_time = current_time;
last_can_update_time = current_time; // 初始化CAN更新时间
last_log_time = current_time; // 初始化日志时间
has_initialized = true;
memset(can_frame, 0, sizeof(can_frame)); // 确保CAN帧初始为全0
WriteLog(current_time, current_vehicle_speed);
// 添加标志表示已完成初始化
printf("System initialized. Speed: %d\n", current_vehicle_speed);
}
if (is_paused) {
return; // 暂停时不更新状态
}
// --- 判断是否需要生成新的CAN信号(100ms周期) ---
if (current_time - last_can_update_time >= can_update_period) {
static uint16_t max_speed = 360;
static uint16_t min_speed = 0;
// 移除保持0速的特殊逻辑,直接使用随机加速度
acceleration = (rand() % 21) - 10; // 加速或减速(-10 到 +10 km/h)
// 更新速度,同时确保速度保持在有效范围内
int32_t new_speed = current_speed + acceleration;
if (new_speed > max_speed) new_speed = max_speed;
if (new_speed < min_speed) new_speed = min_speed;
current_speed = (uint16_t)new_speed;
// 打印新速度
printf("Generated speed = %d\n", current_speed);
// --- 生成 CAN 消息 ---
GenerateCANMessage(current_speed, can_frame);
last_can_update_time = current_time; // 更新 CAN 信号生成时间
}
// --- 从 CAN 信号解码车辆速度 ---
uint16_t decoded_speed = ReadCANMessage(can_frame);
current_vehicle_speed = decoded_speed;
printf("decoded_speed = %d\n", decoded_speed);
// --- 计算时间差并累加行驶距离 ---
uint32_t elapsed_time_ms = current_time - last_update_time;
if (elapsed_time_ms > 0) {
// 确保时间差有效
// 累加行驶距离,但不超过最大值
if (total_distance_mm < max_distance_mm) {
total_distance_mm += (uint64_t)((double)current_vehicle_speed * elapsed_time_ms / 3.6);
if (total_distance_mm > max_distance_mm) {
total_distance_mm = max_distance_mm; // 如果超出最大值,锁定到最大值
}
}
last_update_time = current_time; // 更新最后的累计时间戳
}
// --- 如果满足日志记录周期,则写入日志 ---
if (current_time - last_log_time >= delta_T) {
WriteLog(current_time, current_vehicle_speed); // 写入当前速度到日志
last_log_time = current_time; // 更新日志的时间戳
}
}
// ------------------ 生成 CAN 消息 (Motorola大端序) ------------------
void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]) {
memset(frame, 0, 8); // 清空帧数据
uint16_t mask = (1 << 13) - 1; // 限制速度信息到低 13 位
uint16_t encoded_speed = speed_msg & mask;
// Motorola大端序映射:起始位是信号MSB,物理地址递增方向=信号权重递减方向
for (int i = 0; i < 13; i++) {
uint16_t bit_pos = 16 + i; // 信号物理位置:第16位到第28位
uint8_t byte_pos = bit_pos / 8; // 字节索引
// 修正:直接使用bit_pos % 8(Motorola不需要字节内反转)
uint8_t bit_in_byte = bit_pos % 8;
// 修正:从信号MSB开始提取 (12-i)
if (encoded_speed & (1 << (12 - i))) {
frame[byte_pos] |= (1 << bit_in_byte);
}
}
}
// ------------------ 读取 CAN 消息 (Motorola大端序) ------------------
uint16_t ReadCANMessage(uint8_t frame[8]) {
uint16_t decoded_speed = 0;
// 从物理位读取并重构信号值
for (int i = 0; i < 13; i++) {
uint16_t bit_pos = 16 + i;
uint8_t byte_pos = bit_pos / 8;
uint8_t bit_in_byte = bit_pos % 8; // 修正:直接使用物理位偏移
// 修正:将读取的位放入正确位置 (12-i)
if (frame[byte_pos] & (1 << bit_in_byte)) {
decoded_speed |= (1 << (12 - i));
}
}
return decoded_speed;
}
// ------------------ 后台工作线程 ------------------
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HWND hwnd = (HWND)lpParam;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start_counter);
LARGE_INTEGER current_counter;
uint32_t ideal_loop_time = 0;
uint32_t delay_ms = 500; // 延迟时间(毫秒)
// --- 延迟部分,延迟 500ms 稳定程序 ---
current_vehicle_speed = 0; // 设置初速度为 0,防止累计距离
while (1) {
QueryPerformanceCounter(¤t_counter);
uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart);
if (elapsed_ms >= delay_ms) break; // 延迟完成,继续进入主循环
}
last_update_time = 0; // 重置最后更新时间,防止延迟影响累计逻辑
// --- 主循环 ---
while (1) {
QueryPerformanceCounter(¤t_counter);
uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart);
// 更新车辆状态
UpdateVehicleState(elapsed_ms);
// 通知主线程刷新显示
PostMessage(hwnd, WM_USER + 1, 0, 0);
// 控制日志节奏,保持日志打印间隔严格为 20ms
ideal_loop_time += delta_T;
while (1) {
QueryPerformanceCounter(¤t_counter);
uint32_t temp_elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart);
if (temp_elapsed_ms >= ideal_loop_time) break;
}
}
}
// ------------------ 窗口消息处理 ------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
ClearLogFile();
current_vehicle_speed = 0;
// 创建暂停按钮
HWND pauseButton = CreateWindowW(
L"BUTTON", L"Pause", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
160, 200, 100, 30, hwnd, (HMENU)IDC_PAUSE_BUTTON, NULL, NULL
);
HANDLE thread = CreateThread(NULL, 0, WorkerThread, hwnd, 0, NULL);
if (!thread) {
MessageBox(NULL, L"Error", L"Failed to create thread", MB_ICONERROR);
PostQuitMessage(-1);
}
CloseHandle(thread);
break;
}
case WM_USER + 1: {
wchar_t buffer[256];
// 将总行驶距离从毫米转换为米(四舍五入到米)
uint64_t total_distance_m = (total_distance_mm + 500) / 1000;
// 获取当前系统时间
SYSTEMTIME st;
GetLocalTime(&st);
swprintf(buffer, 256, L"Time: %02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
// 获取客户区大小
RECT clientRect;
GetClientRect(hwnd, &clientRect);
// 定义动态内容区域,避免覆盖按钮部分
RECT dynamicRect = clientRect;
dynamicRect.bottom -= 110; // 保留底部按钮区域
// 获取设备上下文
HDC hdc = GetDC(hwnd);
HDC memDC = CreateCompatibleDC(hdc); // 创建内存DC
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, dynamicRect.right - dynamicRect.left, dynamicRect.bottom - dynamicRect.top);
SelectObject(memDC, memBitmap); // 将位图选入内存DC
// 填充动态内容区域背景
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); // 默认窗口背景色
FillRect(memDC, &dynamicRect, hBrush);
DeleteObject(hBrush);
// 设置字体
HFONT hFont = CreateFontW(
36, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, L"Arial");
HFONT oldFont = SelectObject(memDC, hFont);
// 打印当前时间
RECT timeRect = {
dynamicRect.left + 50,
dynamicRect.top + 10,
dynamicRect.right - 50,
dynamicRect.top + 50 // 时间显示区域
};
SetTextColor(memDC, RGB(0, 0, 0)); // 黑色字体
DrawTextW(memDC, buffer, -1, &timeRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
// 打印车辆速度
RECT speedRect = {
dynamicRect.left + 50,
dynamicRect.top + 70,
dynamicRect.right - 50,
dynamicRect.bottom - 50
};
swprintf(buffer, 256, L"Speed: %d km/h", current_vehicle_speed);
SetTextColor(memDC, RGB(0, 0, 255)); // 蓝色字体
DrawTextW(memDC, buffer, -1, &speedRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
// 打印总行驶距离
RECT distanceRect = {
dynamicRect.left + 50,
dynamicRect.top + 150,
dynamicRect.right - 50,
dynamicRect.bottom - 30
};
if (total_distance_m >= 100000) {
swprintf(buffer, 256, L"Distance: %.1f km", total_distance_m / 1000.0); // 显示为 km(保留1位小数)
} else {
swprintf(buffer, 256, L"Distance: %d m", (int)total_distance_m); // 显示为米
}
SetTextColor(memDC, RGB(255, 0, 0)); // 红色字体
DrawTextW(memDC, buffer, -1, &distanceRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
// 将内存DC的内容复制到动态区域
BitBlt(hdc, dynamicRect.left, dynamicRect.top, dynamicRect.right - dynamicRect.left, dynamicRect.bottom - dynamicRect.top, memDC, 0, 0, SRCCOPY);
// 清理资源
SelectObject(memDC, oldFont);
DeleteObject(hFont);
DeleteDC(memDC);
DeleteObject(memBitmap);
ReleaseDC(hwnd, hdc);
break;
}
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_PAUSE_BUTTON) {
is_paused = !is_paused;
HWND pauseButton = GetDlgItem(hwnd, IDC_PAUSE_BUTTON);
SetWindowTextW(pauseButton, is_paused ? L"Resume" : L"Pause");
}
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default: {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return 0;
}
// ------------------ 主程序入口 ------------------
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
WNDCLASSW wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = L"VehicleLogger";
if (!RegisterClassW(&wc)) return -1;
HWND hwnd = CreateWindowW(
L"VehicleLogger", L"CAN Signal Logger", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 450, 300, NULL, NULL, hInstance, NULL
);
if (!hwnd) return -1;
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
一行行解释
最新发布