SerialConnect: implement XMODEM send.

This commit is contained in:
Adrien Destugues 2017-07-16 10:21:22 +02:00
parent b32b6a8633
commit ce058fa0a9
7 changed files with 293 additions and 24 deletions

View File

@ -10,6 +10,7 @@ Application SerialConnect :
SerialApp.cpp
SerialWindow.cpp
TermView.cpp
XModem.cpp
encoding.c
input.c
parser.c

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
* Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
* Distributed under the terms of the MIT licence.
*/
@ -65,6 +65,7 @@ const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
SerialApp::SerialApp()
: BApplication(SerialApp::kApplicationSignature)
, fLogFile(NULL)
, fFileSender(NULL)
{
fWindow = new SerialWindow();
@ -78,6 +79,7 @@ SerialApp::SerialApp()
SerialApp::~SerialApp()
{
delete fLogFile;
delete fFileSender;
}
@ -99,21 +101,32 @@ void SerialApp::MessageReceived(BMessage* message)
} else {
fSerialPort.Close();
}
// Forward to the window so it can enable/disable menu items
fWindow->PostMessage(message);
return;
}
case kMsgDataRead:
{
// forward the message to the window, which will display the
// incoming data
fWindow->PostMessage(message);
const char* bytes;
ssize_t length;
message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length);
if (fLogFile) {
const char* bytes;
ssize_t length;
message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length);
if (fLogFile->Write(bytes, length) != length) {
// TODO error handling
if (fFileSender != NULL) {
if (fFileSender->BytesReceived(bytes, length)) {
delete fFileSender;
fFileSender = NULL;
}
} else {
// forward the message to the window, which will display the
// incoming data
fWindow->PostMessage(message);
if (fLogFile) {
if (fLogFile->Write(bytes, length) != length) {
// TODO error handling
}
}
}
@ -121,6 +134,10 @@ void SerialApp::MessageReceived(BMessage* message)
}
case kMsgDataWrite:
{
// Do not allow sending if a file transfer is in progress.
if (fFileSender != NULL)
return;
const char* bytes;
ssize_t size;
@ -147,6 +164,25 @@ void SerialApp::MessageReceived(BMessage* message)
debugger("Invalid BMessage received");
return;
}
case kMsgSendXmodem:
{
entry_ref ref;
if (message->FindRef("refs", &ref) == B_OK) {
BFile* file = new BFile(&ref, B_READ_ONLY);
status_t error = file->InitCheck();
if (error != B_OK)
puts(strerror(error));
else {
delete fFileSender;
fFileSender = new XModemSender(file, &fSerialPort, fWindow);
}
} else {
message->PrintToStream();
debugger("Invalid BMessage received");
}
return;
}
case kMsgCustomBaudrate:
{
// open the custom baudrate selector window

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
* Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
* Distributed under the terms of the MIT licence.
*/
@ -13,6 +13,8 @@
#include <SerialPort.h>
#include <String.h>
#include "XModem.h"
class BFile;
class SerialWindow;
@ -44,6 +46,8 @@ class SerialApp: public BApplication
BFile* fLogFile;
BString fPortPath;
XModemSender* fFileSender;
static status_t PollSerial(void*);
static const BPropertyInfo kScriptingProperties;
@ -57,7 +61,9 @@ enum messageConstants {
kMsgDataWrite = 'dawr',
kMsgLogfile = 'logf',
kMsgOpenPort = 'open',
kMsgProgress = 'prog',
kMsgSettings = 'stty',
kMsgSendXmodem = 'xmtx',
};
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015, Adrien Destugues, pulkomandy@pulkomandy.tk
* Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence.
*/
@ -15,6 +15,7 @@
#include <MenuItem.h>
#include <ScrollView.h>
#include <SerialPort.h>
#include <StatusBar.h>
#include "SerialApp.h"
#include "TermView.h"
@ -63,18 +64,27 @@ SerialWindow::SerialWindow()
ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1);
r = fTermView->Frame();
r.top = r.bottom - 37;
fStatusBar = new BStatusBar(r, "file transfer progress", NULL, NULL);
fStatusBar->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
fStatusBar->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fStatusBar->Hide();
AddChild(menuBar);
AddChild(fTermView);
AddChild(scrollBar);
AddChild(fStatusBar);
fConnectionMenu = new BMenu("Connection");
BMenu* fileMenu = new BMenu("File");
fFileMenu = new BMenu("File");
BMenu* settingsMenu = new BMenu("Settings");
fConnectionMenu->SetRadioMode(true);
menuBar->AddItem(fConnectionMenu);
menuBar->AddItem(fileMenu);
menuBar->AddItem(fFileMenu);
menuBar->AddItem(settingsMenu);
// TODO edit menu - what's in it ?
@ -83,15 +93,17 @@ SerialWindow::SerialWindow()
BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
new BMessage(kMsgLogfile));
fileMenu->AddItem(logFile);
#if 0
fFileMenu->AddItem(logFile);
// TODO implement these
BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS,
NULL);
fileMenu->AddItem(xmodemSend);
BMenuItem* xmodemSend = new BMenuItem("XModem send" B_UTF8_ELLIPSIS,
new BMessage(kMsgSendXmodem));
fFileMenu->AddItem(xmodemSend);
xmodemSend->SetEnabled(false);
#if 0
BMenuItem* xmodemReceive = new BMenuItem(
"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
fileMenu->AddItem(xmodemReceive);
fFileMenu->AddItem(xmodemReceive);
xmodemReceive->SetEnabled(false);
#endif
// Configuring all this by menus may be a bit unhandy. Make a setting
@ -279,6 +291,18 @@ void SerialWindow::MessageReceived(BMessage* message)
{
switch (message->what)
{
case kMsgOpenPort:
{
BString path;
bool open = (message->FindString("port name", &path) == B_OK);
int i = 1; // Skip "log to file", which woeks even when offline.
BMenuItem* item;
while((item = fFileMenu->ItemAt(i++)))
{
item->SetEnabled(open);
}
return;
}
case kMsgDataRead:
{
const char* bytes;
@ -289,11 +313,13 @@ void SerialWindow::MessageReceived(BMessage* message)
return;
}
case kMsgLogfile:
case kMsgSendXmodem:
{
// Let's lazy init the file panel
if (fLogFilePanel == NULL) {
fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
NULL, B_FILE_NODE, false);
fLogFilePanel = new BFilePanel(
message->what == kMsgSendXmodem ? B_OPEN_PANEL : B_SAVE_PANEL,
&be_app_messenger, NULL, B_FILE_NODE, false);
fLogFilePanel->SetMessage(message);
}
fLogFilePanel->Show();
@ -391,6 +417,30 @@ void SerialWindow::MessageReceived(BMessage* message)
return;
}
case kMsgProgress:
{
// File transfer progress
int32 pos = message->FindInt32("pos");
int32 size = message->FindInt32("size");
BString label = message->FindString("info");
if (pos >= size) {
if (!fStatusBar->IsHidden()) {
fStatusBar->Hide();
fTermView->ResizeBy(0, fStatusBar->Bounds().Height() - 1);
}
} else {
BString text;
text.SetToFormat("%" B_PRId32 "/%" B_PRId32, pos, size);
fStatusBar->SetMaxValue(size);
fStatusBar->SetTo(pos, label, text);
if (fStatusBar->IsHidden()) {
fStatusBar->Show();
fTermView->ResizeBy(0, -(fStatusBar->Bounds().Height() - 1));
}
}
return;
}
}
BWindow::MessageReceived(message);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015, Adrien Destugues, pulkomandy@pulkomandy.tk
* Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence.
*/
@ -9,6 +9,7 @@
class BFilePanel;
class BMenu;
class BStatusBar;
class TermView;
@ -32,7 +33,9 @@ class SerialWindow: public BWindow
BMenu* fFlowcontrolMenu;
BMenu* fBaudrateMenu;
BMenu* fLineTerminatorMenu;
BMenu* fFileMenu;
BFilePanel* fLogFilePanel;
BStatusBar* fStatusBar;
static const int kBaudrates[];
static const int kBaudrateConstants[];

View File

@ -0,0 +1,129 @@
/*
* Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#include "XModem.h"
#include "SerialApp.h"
#include <String.h>
#include <stdio.h>
#include <string.h>
// ASCII control characters used in XMODEM protocol
static const char kSOH = 1;
static const char kEOT = 4;
static const char kACK = 6;
static const char kNAK = 21;
static const char kSUB = 26;
static const int kBlockSize = 128;
XModemSender::XModemSender(BDataIO* source, BSerialPort* sink, BHandler* listener)
: fSource(source),
fSink(sink),
fListener(listener),
fBlockNumber(0),
fEotSent(false)
{
fStatus = "Waiting for receiver" B_UTF8_ELLIPSIS;
BPositionIO* pos = dynamic_cast<BPositionIO*>(source);
if (pos)
pos->GetSize(&fSourceSize);
else
fSourceSize = 0;
NextBlock();
}
XModemSender::~XModemSender()
{
delete fSource;
}
bool
XModemSender::BytesReceived(const char* data, size_t length)
{
size_t i;
for (i = 0; i < length; i++)
{
switch (data[i])
{
case kNAK:
if (fEotSent) {
fSink->Write(&kEOT, 1);
} else {
fStatus = "Checksum error, re-send block";
SendBlock();
}
break;
case kACK:
if (fEotSent) {
return true;
}
if (NextBlock() == B_OK) {
fStatus = "Sending" B_UTF8_ELLIPSIS;
SendBlock();
} else {
fStatus = "Everything sent, waiting for acknowledge";
fSink->Write(&kEOT, 1);
fEotSent = true;
}
break;
default:
break;
}
}
return false;
}
void
XModemSender::SendBlock()
{
uint8_t header[3];
uint8_t checksum = 0;
int i;
header[0] = kSOH;
header[1] = fBlockNumber;
header[2] = 255 - fBlockNumber;
for (i = 0; i < kBlockSize; i++)
checksum += fBuffer[i];
fSink->Write(header, 3);
fSink->Write(fBuffer, kBlockSize);
fSink->Write(&checksum, 1);
}
status_t
XModemSender::NextBlock()
{
memset(fBuffer, kSUB, kBlockSize);
if (fSource->Read(fBuffer, kBlockSize) > 0) {
fBlockNumber++;
BMessage msg(kMsgProgress);
msg.AddInt32("pos", fBlockNumber);
msg.AddInt32("size", fSourceSize / kBlockSize);
msg.AddString("info", fStatus);
fListener.SendMessage(&msg);
return B_OK;
}
return B_ERROR;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#ifndef XMODEM_H
#define XMODEM_H
#include <Messenger.h>
#include <String.h>
class BDataIO;
class BHandler;
class BSerialPort;
class XModemSender {
public:
XModemSender(BDataIO* source, BSerialPort* sink,
BHandler* listener);
~XModemSender();
bool BytesReceived(const char* data, size_t length);
private:
void SendBlock();
status_t NextBlock();
private:
BDataIO* fSource;
BSerialPort* fSink;
BMessenger fListener;
off_t fBlockNumber;
off_t fSourceSize;
uint8_t fBuffer[128];
bool fEotSent;
BString fStatus;
};
#endif /* !XMODEM_H */