Improved decoding and screen presentation (especially the scroll region at the bottom).

This commit is contained in:
2026-01-18 15:45:49 -06:00
parent f7826bd69f
commit 8cd05e5e7e
2 changed files with 290 additions and 77 deletions

View File

@@ -9,6 +9,7 @@
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <timeapi.h>
#include "SerialPort\SerialPort.h"
enum
@@ -21,6 +22,8 @@ CSerialPort avr;
int avrOnPort = COM_NO_PORT; // Serial Port 1, 2, ... taking note that some USB adapters can be up toward channel 11, 12, ...
unsigned avrBaud = 9600;
DWORD const retryInterval = 1000; // ms
// Each DT is the hex-character from the stream
//
//
@@ -234,36 +237,92 @@ char progname[MAXTEXTLEN];
int AttachToSerialPort();
int DetachSerialPort();
void ProcessSerialReceive(void);
// SerialSend
//
// Try to send the message.
//
// @param[in] p is a pointer to the message to send
// @param[in] len is the count of bytes in the message
// @returns true if the serial interface accepted it.
//
bool SerialSend(const uint8_t *p, DWORD len);
#define SERIALQUEUESIZE 3
typedef struct {
uint8_t *messageToSend;
DWORD len;
} SerialQueue_T;
static SerialQueue_T serialQueue[SERIALQUEUESIZE];
static int serialQueueCount = 0;
// ProcessSerialQueue
//
// If there are parameters passed, insert a message into the queue.
// Process the queue (with zero or more messages) to send
//
// @param[in] p is a pointer to the message to send
// @param[in] len is the count of bytes in the message
// @returns false if the queue (which is a fixed size) is full.
//
bool ProcessSerialQueue(const uint8_t *p = NULL, DWORD len = 0);
// ProcessSerialReceive
//
// See if anything came in, and if so, process it.
//
// @returns true if a message was processed.
//
bool ProcessSerialReceive(void);
void EnumerateComPorts();
unsigned long Hex2Dec(uint8_t *p, int dig);
bool UserWantsToExitCanSniff = false;
DWORD progStartTime;
// For special cursor positioning
bool consoleInit = false;
HANDLE hStdout, hStdin;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
CONSOLE_SCREEN_BUFFER_INFO csbi;
WORD wOldColorAttrs; // Save the current console colors
short consoleWidth = 105;
short consoleHeight = 70;
short consoleScrollHeight = 14;
// 0 99
// +---------------------------------------------------------------+
// | Program banner information | 0
// | on two lines |
// | Current status information starts on line 3 |
// | and down a ways... |
// | |
// | |
// | |
// + Down near the bottom is the scroll region + 69
// | |
// | | 79
// +---------------------------------------------------------------+
const int scrollTop = 59;
const int scrollBot = 69;
void Console_Init();
void Console_Cls();
void Console_SetCursor(short x, short y);
void Console_Write(char *text);
void Console_WriteAt(short x, short y, char *text);
void Console_Write(const char *text);
void Console_WriteAt(short x, short y, const char *text);
void Console_ScrollBottomRegion(short topLine, short bottomLine);
bool Console_AdvanceToNextLineIfNotRoomFor(short x, int scroll = -1);
uint8_t LastSentMessage[MAXTEXTLEN];
uint16_t LastSentMessageLen;
uint64_t LastSentMessageTime; // when the last message was sent
typedef struct {
const char Char;
const char *pDescription;
const char *pCommand;
int cmdLen;
DWORD cmdLen;
} UserCmd_T;
const UserCmd_T UserCommands[] = {
@@ -324,21 +383,26 @@ void ProcessWindowsMessage(void) {
// EmitBuffer
//
// Emits the provided buffer to the console, translating non-printable
// characters into hex codes enclosed in angle brackets.
// characters into hex codes enclosed in square brackets.
//
// Consider line-wrapping to align the next line
//
void EmitBuffer(const char *prefix, const uint8_t *buf, bool appendReturn = false) {
void EmitBuffer(const char *prefix, const uint8_t *buf, size_t len = 0, bool appendReturn = false) {
int i = 0;
const char *p = (const char *)buf;
printf("[%3d]%s", (int)strlen(p), prefix);
while (*p) {
DWORD now = timeGetTime();
char txtBuf[MAXTEXTLEN] = "";
if (len == 0) len = strlen((const char *)buf);
sprintf_s(txtBuf, MAXTEXTLEN, "%7.3f: [%3d]%s", (float)(now - progStartTime)/1000.0f, (int)strlen(p), prefix);
Console_Write(txtBuf);
while (*p && ((unsigned)(p - (const char *)buf) < len)) {
if (isprint(*p)) {
putchar(*p);
i++;
} else if (*p == '\r') {
putchar('\n');
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
} else if (*p == '\n') {
// skip it
} else {
@@ -346,7 +410,8 @@ void EmitBuffer(const char *prefix, const uint8_t *buf, bool appendReturn = fals
}
if ((i & 3) == 0) {
if (Console_AdvanceToNextLineIfNotRoomFor(12, 1)) {
printf(" ");
//Console_ScrollBottomRegion(scrollTop, scrollBot);
printf(" "); // sized to get past the timestamp, length, and prefix
i = 0;
} else {
printf(" ");
@@ -355,34 +420,64 @@ void EmitBuffer(const char *prefix, const uint8_t *buf, bool appendReturn = fals
p++;
}
if (appendReturn) {
putch('\r');
Console_ScrollBottomRegion(scrollTop, scrollBot); // putch('\r');
} else {
putch('\n');
Console_ScrollBottomRegion(-14, -1);
//putch('\n');
//Console_ScrollBottomRegion(scrollTop, scrollBot);
}
}
void EchoSerialRecv(const uint8_t *pMsg) {
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
EmitBuffer("< ", pMsg);
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
}
void SerialSend(const uint8_t *p, int len) {
Console_SetCursor(0, -3);
EmitBuffer("> ", p);
Console_ScrollBottomRegion(-14, -1);
if (avr.Write((const LPVOID)p, len) != len) {
Console_SetCursor(0, -3);
Console_Write("***** Failed to send. Port not open?");
Console_ScrollBottomRegion(-14, -1);
bool SerialSend(const uint8_t *p, DWORD len) {
bool retVal = false;
Console_SetCursor(0, -1);
EmitBuffer("> ", p, len);
Console_ScrollBottomRegion(scrollTop, scrollBot);
if (avr.Write((const LPVOID)p, len) == len) {
retVal = true;
} else {
// If we send something, we expect a response within ?200ms? and if not we should try again, up to 5 times.
memcpy(LastSentMessage, p, len);
LastSentMessageTime = GetTickCount64();
LastSentMessageLen = len;
Console_SetCursor(0, -1);
Console_Write("***** Failed to send. Port not open?");
Console_ScrollBottomRegion(scrollTop, scrollBot);
}
return retVal;
}
bool ProcessSerialQueue(const uint8_t *p, DWORD len) {
bool retVal = false; // assume fail
static bool freshData = false;
//DWORD now = timeGetTime();
//static DWORD lastSendTime = timeGetTime();
if (p && len) {
if (serialQueueCount < SERIALQUEUESIZE) {
serialQueue[serialQueueCount].messageToSend = (uint8_t *)malloc(len);
if (serialQueue[serialQueueCount].messageToSend) {
memcpy(serialQueue[serialQueueCount].messageToSend, p, len);
serialQueue[serialQueueCount].len = len;
serialQueueCount++;
retVal = true;
freshData = true;
}
}
}
if (serialQueueCount) {
if (SerialSend((const uint8_t *)serialQueue[0].messageToSend, serialQueue[0].len)) {
--serialQueueCount;
free(serialQueue[0].messageToSend);
for (int i = 0; i < serialQueueCount; i++) {
serialQueue[i] = serialQueue[i + 1];
}
retVal = true;
}
}
return retVal;
}
@@ -412,13 +507,13 @@ void EmitRuntimeHelp() {
Console_Write(buf);
}
if (avr.IsOpen()) {
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
printf(" Com Status: RTS: %-3s ", avr.Get_RTS_State() ? "ON" : "OFF");
printf("DTR: %-3s ", avr.Get_DTR_State() ? "ON" : "OFF");
printf("CTS: %-3s ", avr.Get_CTS_State() ? "ON" : "OFF");
printf("DSR: %-3s ", avr.Get_DSR_State() ? "ON" : "OFF");
printf("RI: %-3s\n", avr.Get_RI_State() ? "ON" : "OFF");
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
}
}
@@ -450,8 +545,8 @@ static void PCMessage(const char *msg, int len, uint8_t **src, const char * (fnc
msg,
p
);
Console_AdvanceToNextLineIfNotRoomFor(26);
Console_Write(outBuf);
Console_AdvanceToNextLineIfNotRoomFor(28);
}
@@ -491,6 +586,75 @@ const char * InputText(uint32_t val) {
}
}
const char *ProgramName(uint8_t val) {
val &= 0x7F; // Not accommodating the other variants yet
const char *nameList[] = {
"Hall A(HALL1)",
"Hall B",
"Hall C",
"unk",
"Hall C",
"Hall E",
"Live Concert",
"unk",
"Tokyo",
"Freiburg",
"Royaumont",
"unk",
"Village Gate",
"Village Vanguard",
"The Bottom Line",
"unk",
"The Roxy Theater",
"Warehouse Loft",
"Arena",
"unk",
"Disco",
"Party",
"Game",
"6 / 8CH Stereo",
"Pop / Rock",
"DJ",
"unk",
"unk",
"Opera",
"Pavillion",
"unk",
"unk",
"Mono Movie",
"Variety Sports",
"unk",
"unk",
"Spectacre",
"Sci - Fi",
"unk",
"unk",
"Adventure",
"General",
"unk",
"unk",
"Normal",
"Enhanced",
"unk",
"unk",
"PLII Movie",
"PLII Music",
"Neo: 6 Movie",
"Neo: 6 Music",
"STEREO A 2CH Stereo",
"STEREO B 2CH Direct Stereo"
"THX A Cinema",
"THX B Music",
};
if (val < sizeof(nameList)) {
return nameList[val];
} else {
return "huh?";
}
}
// VolumeDB
//
// Convert the communication units of volume into db
@@ -521,7 +685,7 @@ const char *VolumeDB(uint32_t val) {
void ShowAll(void) {
if (avrStatus.headerValid) {
Console_SetCursor(0, 3);
Console_SetCursor(0, 2);
char buf[MAXTEXTLEN];
sprintf_s(buf, MAXTEXTLEN, "Model ID: %c%c%c%c%c, ver: %c\n", avrStatus.header.type[0], avrStatus.header.type[1],
avrStatus.header.type[2], avrStatus.header.type[3], avrStatus.header.type[4],
@@ -637,7 +801,7 @@ void ProcessKeyboard(void) {
static uint32_t spin = 0;
bool cmdFound = false;
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
if (spin++ % 16 == 0)
EmitSpinner();
if (CS_KeyHit()) {
@@ -654,28 +818,28 @@ void ProcessKeyboard(void) {
break;
case 'S':
avr.Set_RTS_State(TRUE);
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
printf("RTS set ON\n");
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
break;
case 's':
avr.Set_RTS_State(FALSE);
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
printf("RTS set OFF\n");
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
break;
default:
for (int i = 0; i < sizeof(UserCommands) / sizeof(UserCmd_T); i++) {
if (UserCommands[i].Char == c) {
SerialSend((const uint8_t *)UserCommands[i].pCommand, UserCommands[i].cmdLen);
ProcessSerialQueue((const uint8_t *)UserCommands[i].pCommand, UserCommands[i].cmdLen);
cmdFound = true;
break;
}
}
if (!cmdFound) {
Console_SetCursor(0, -3);
Console_SetCursor(0, -1);
printf("Unrecognized command '%c' (0x%02X)\n", isprint(c) ? c : '.', (unsigned char)c);
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
}
break;
}
@@ -699,7 +863,7 @@ void GetProgName(char *name) {
}
void EmitHelp() {
void EmitCommandLineHelp() {
printf("%s [options] by Smartware Computing\n", progname);
printf(" options:\n");
printf(" -C=X[,yyyy] Set to Com port X to baud rate yyyy\n");
@@ -718,6 +882,7 @@ void EmitHelp() {
/******************************************************/
int __cdecl main(int argc, char *argv[]) {
progStartTime = timeGetTime();
Console_Init();
GetProgName(argv[0]);
@@ -732,12 +897,12 @@ int __cdecl main(int argc, char *argv[]) {
avrBaud = param2;
} else {
printf("***** Unrecognized command '%s' *****\n", argv[i]);
EmitHelp();
EmitCommandLineHelp();
exit(EXIT_IllegalOption);
}
}
if (argc == 0 || avrOnPort == COM_NO_PORT) {
EmitHelp();
EmitCommandLineHelp();
}
Console_Cls();
@@ -750,7 +915,8 @@ int __cdecl main(int argc, char *argv[]) {
printf("Attached to Serial Port on COM%i at %i baud\n", avrOnPort, avrBaud);
do {
ProcessKeyboard();
ProcessSerialReceive();
/* bool anyRcvdMsg = */ ProcessSerialReceive();
ProcessSerialQueue();
ProcessWindowsMessage();
} while (!UserWantsToExitCanSniff);
DetachSerialPort();
@@ -815,14 +981,23 @@ void ProcessResponse(uint8_t type, uint8_t guard, uint16_t cmd, uint16_t data) {
char buf[MAXTEXTLEN];
switch (cmd) {
case 0x26:
sprintf_s(buf, MAXTEXTLEN, "Vol: %s", VolumeDB(data));
sprintf_s(buf, MAXTEXTLEN, "Vol: %s\n", VolumeDB(data));
SetData(&avrStatus.config.DT15, Dec2Hex(data, 2));
Console_Write(buf);
Console_ScrollBottomRegion(scrollTop, scrollBot);
ShowAll();
break;
case 0x28:
sprintf_s(buf, MAXTEXTLEN, "Prog: %s\n", ProgramName(data));
SetData(&avrStatus.config.DT19, Dec2Hex(data, 2));
Console_Write(buf);
Console_ScrollBottomRegion(scrollTop, scrollBot);
ShowAll();
break;
default:
sprintf_s(buf, MAXTEXTLEN, "type: %X, guard: %X, cmd: %02X, data: %02X", type, guard, cmd, data);
Console_Write(buf);
Console_ScrollBottomRegion(scrollTop, scrollBot);
break;
}
}
@@ -902,7 +1077,8 @@ void AnalyzeResponse(uint8_t *szBuffer, uint32_t num) {
// All character sequences end with \x03 (this is the equiv of a <cr>)
//
//
void ProcessSerialReceive() {
bool ProcessSerialReceive() {
bool anythingReceived = false;
uint8_t rcv_buff[MAXTEXTLEN] = { 0 };
static uint8_t messageBuf[MAXTEXTLEN] = { 0 };
static uint8_t *p = messageBuf; // used to fill the rcv_buff as data comes in
@@ -921,20 +1097,14 @@ void ProcessSerialReceive() {
p = messageBuf; // Reset the buffer for the next message, which might be in rcv_buff
*p = '\0';
anythingReceived = true;
}
}
if (messageBuf[0]) {
//EmitBuffer("~", messageBuf, true); // Show them the partial receipt if anything is there
}
} else {
// If we're waiting for a response to something we sent, and it's been more than 200ms, resend it
if (LastSentMessageTries > 0) {
if ((GetTickCount64() - LastSentMessageTime) > 200) {
LastSentMessageLen && (GetTickCount64() - LastSentMessageTime) > 200);
SerialSend(LastSentMessage, LastSentMessageLen);
}
//EmitBuffer("~", messageBuf, 0, true); // Show them the partial receipt if anything is there
}
}
return anythingReceived;
}
@@ -997,8 +1167,47 @@ int AttachToSerialPort() {
void Console_Init() {
hStdin = GetStdHandle(STD_INPUT_HANDLE);
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
wOldColorAttrs = csbiInfo.wAttributes;
if (!GetConsoleScreenBufferInfo(hStdout, &csbi)) {
fprintf(stderr, "Error: Unable to get console buffer info. Code: %lu\n", GetLastError());
exit(1);
}
// Desired size (width x height)
COORD newSize;
newSize.X = consoleWidth; // columns
newSize.Y = consoleHeight; // rows
// Step 1: Shrink window if needed before shrinking buffer
SMALL_RECT tempWindow = csbi.srWindow;
tempWindow.Right = tempWindow.Left + (newSize.X - 1);
tempWindow.Bottom = tempWindow.Top + (newSize.Y - 1);
if (!SetConsoleWindowInfo(hStdout, TRUE, &tempWindow)) {
fprintf(stderr, "Error: Unable to temporarily resize window. Code: %lu\n", GetLastError());
exit(1);
}
// Step 2: Set new buffer size
if (!SetConsoleScreenBufferSize(hStdout, newSize)) {
fprintf(stderr, "Error: Unable to set console buffer size. Code: %lu\n", GetLastError());
exit(1);
}
// Step 3: Set final window size to match buffer
SMALL_RECT newWindow;
newWindow.Left = 0;
newWindow.Top = 0;
newWindow.Right = newSize.X - 1;
newWindow.Bottom = newSize.Y - 1;
if (!SetConsoleWindowInfo(hStdout, TRUE, &newWindow)) {
fprintf(stderr, "Error: Unable to set console window size. Code: %lu\n", GetLastError());
exit(1);
}
// end
wOldColorAttrs = csbi.wAttributes;
//SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY);
}
@@ -1009,7 +1218,6 @@ void Console_Close() {
void Console_Cls() {
COORD coordScreen = { 0, 0 }; // home for the cursor
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
// Get the number of character cells in the current buffer.
@@ -1041,8 +1249,12 @@ void Console_Cls() {
SetConsoleCursorPosition(hStdout, coordScreen);
}
void Console_Write(char *text) {
void Console_Write(const char *text) {
DWORD written;
GetConsoleScreenBufferInfo(hStdout, &csbi);
if (csbi.dwCursorPosition.Y >= (csbi.srWindow.Bottom - consoleScrollHeight)) {
//Console_ScrollBottomRegion(scrollTop, scrollBot);
}
WriteConsole(hStdout, text, (DWORD)strlen(text), &written, nullptr);
}
@@ -1053,33 +1265,34 @@ void Console_WriteAt(short x, short y, char *text) {
}
void Console_SetCursor(short x, short y) {
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
csbiInfo.dwCursorPosition.X = (short)((x < 0) ? csbiInfo.srWindow.Right + x : x);
csbiInfo.dwCursorPosition.Y = (short)((y < 0) ? csbiInfo.srWindow.Bottom + y : y);
SetConsoleCursorPosition(hStdout, csbiInfo.dwCursorPosition);
GetConsoleScreenBufferInfo(hStdout, &csbi);
csbi.dwCursorPosition.X = (short)((x < 0) ? csbi.srWindow.Right + x : x);
csbi.dwCursorPosition.Y = (short)((y < 0) ? csbi.srWindow.Bottom + y : y);
SetConsoleCursorPosition(hStdout, csbi.dwCursorPosition);
}
void Console_ScrollBottomRegion(short topLine, short bottomLine) {
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
if (topLine < 0) topLine = csbiInfo.srWindow.Bottom + topLine;
if (bottomLine < 0) bottomLine = csbiInfo.srWindow.Bottom + bottomLine;
SMALL_RECT scrollRect = { 0, topLine, csbiInfo.dwMaximumWindowSize.X, bottomLine };
GetConsoleScreenBufferInfo(hStdout, &csbi);
if (topLine < 0) topLine = csbi.srWindow.Bottom + topLine;
if (bottomLine < 0) bottomLine = csbi.srWindow.Bottom + bottomLine;
SMALL_RECT scrollRect = { 0, topLine, csbi.dwMaximumWindowSize.X-1, bottomLine };
COORD dest = { 0, topLine - 1 };
CHAR_INFO fill = { ' ', { FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE } };
ScrollConsoleScreenBuffer(hStdout, &scrollRect, nullptr, dest, &fill);
ScrollConsoleScreenBuffer(hStdout, &scrollRect, &scrollRect, dest, &fill);
putch('\r'); // Cursor to left margin
}
bool Console_AdvanceToNextLineIfNotRoomFor(short x, int scroll) {
bool advanced = false;
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
if (csbiInfo.dwCursorPosition.X + x > csbiInfo.dwMaximumWindowSize.X) {
GetConsoleScreenBufferInfo(hStdout, &csbi);
if (csbi.dwCursorPosition.X + x > csbi.dwMaximumWindowSize.X) {
advanced = true;
if (scroll == 1) {
Console_ScrollBottomRegion(-14, -1);
Console_ScrollBottomRegion(scrollTop, scrollBot);
} else {
csbiInfo.dwCursorPosition.Y++;
csbi.dwCursorPosition.Y++;
}
Console_SetCursor(0, csbiInfo.dwCursorPosition.Y);
Console_SetCursor(0, csbi.dwCursorPosition.Y);
}
return advanced;
}