Add the skeleton of the new client app.

This commit is contained in:
David Given
2018-10-20 13:28:20 +02:00
parent 8648405710
commit 7efaae2f76
13 changed files with 568 additions and 0 deletions

107
lib/flags.cc Normal file
View File

@@ -0,0 +1,107 @@
#include "globals.h"
#include "flags.h"
static std::vector<Flag*> all_flags;
static std::map<const std::string, Flag*> flags_by_name;
Flag::Flag(const std::vector<std::string>& names, const std::string helptext):
_names(names),
_helptext(helptext)
{
for (auto& name : names)
{
if (flags_by_name.find(name) != flags_by_name.end())
Error() << "two flags use the name '" << name << "'";
flags_by_name[name] = this;
}
all_flags.push_back(this);
}
void Flag::parseFlags(int argc, const char* argv[])
{
int index = 1;
while (index < argc)
{
std::string thisarg = argv[index];
std::string thatarg = (index < (argc-1)) ? argv[index+1] : "";
std::string key;
std::string value;
bool usesthat = false;
if ((thisarg.size() == 0) || (thisarg[0] != '-'))
Error() << "non-option parameter " << thisarg << " seen (try --help)";
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
{
/* Long option. */
auto equals = thisarg.rfind('=');
if (equals != std::string::npos)
{
key = thisarg.substr(0, equals);
value = thisarg.substr(equals+1);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
else
{
/* Short option. */
if (thisarg.size() > 2)
{
key = thisarg.substr(0, 2);
value = thisarg.substr(2);
}
else
{
key = thisarg;
value = thatarg;
usesthat = true;
}
}
auto flag = flags_by_name.find(key);
if (flag == flags_by_name.end())
Error() << "unknown flag '" << key << "'; try --help";
flag->second->set(value);
index++;
if (usesthat && flag->second->hasArgument())
index++;
}
}
static void doHelp()
{
std::cout << "FluxEngine options:" << std::endl;
for (auto flag : all_flags)
{
std::cout << " ";
bool firstname = true;
for (auto name : flag->names())
{
if (!firstname)
std::cout << ", ";
std::cout << name;
firstname = false;
}
if (flag->hasArgument())
std::cout << " <default: \"" << flag->defaultValue() << "\">";
std::cout << ": " << flag->helptext() << std::endl;
}
exit(0);
}
static ActionFlag helpFlag = ActionFlag(
{ "--help", "-h" },
"Shows the help.",
doHelp);

103
lib/flags.h Normal file
View File

@@ -0,0 +1,103 @@
#ifndef FLAGS_H
#define FLAGS_H
class Flag
{
public:
static void parseFlags(int argc, const char* argv[]);
Flag(const std::vector<std::string>& names, const std::string helptext);
virtual ~Flag() {};
const std::string& name() const { return _names[0]; }
const std::vector<std::string> names() const { return _names; }
const std::string& helptext() const { return _helptext; }
virtual bool hasArgument() const = 0;
virtual const std::string defaultValue() const = 0;
virtual void set(const std::string value) = 0;
private:
const std::vector<std::string> _names;
const std::string _helptext;
};
class ActionFlag : Flag
{
public:
ActionFlag(const std::vector<std::string>& names, const std::string helptext,
std::function<void(void)> callback):
Flag(names, helptext),
_callback(callback)
{}
bool hasArgument() const { return false; }
const std::string defaultValue() const { return ""; }
void set(const std::string value) { _callback(); }
private:
const std::function<void(void)> _callback;
};
class SettableFlag : public Flag
{
public:
SettableFlag(const std::vector<std::string>& names, const std::string helptext):
Flag(names, helptext)
{}
operator bool() const { return _value; }
bool hasArgument() const { return false; }
const std::string defaultValue() const { return "false"; }
void set(const std::string value) { _value = true; }
private:
bool _value = false;
};
template <typename T>
class ValueFlag : public Flag
{
public:
ValueFlag(const std::vector<std::string>& names, const std::string helptext,
const T defaultValue):
Flag(names, helptext),
_defaultValue(defaultValue),
_value(defaultValue)
{}
operator T() const { return _value; }
bool hasArgument() const { return true; }
protected:
T _defaultValue;
T _value;
};
class StringFlag : public ValueFlag<std::string>
{
public:
StringFlag(const std::vector<std::string>& names, const std::string helptext,
const std::string defaultValue = ""):
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return _defaultValue; }
void set(const std::string value) { _value = value; }
};
class IntFlag : public ValueFlag<int>
{
public:
IntFlag(const std::vector<std::string>& names, const std::string helptext,
int defaultValue = 0):
ValueFlag(names, helptext, defaultValue)
{}
const std::string defaultValue() const { return std::to_string(_defaultValue); }
void set(const std::string value) { _value = std::stoi(value); }
};
#endif

11
lib/globals.cc Normal file
View File

@@ -0,0 +1,11 @@
#include "globals.h"
#include <sys/time.h>
double getCurrentTime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return double(tv.tv_sec) + tv.tv_usec/1000000.0;
}

35
lib/globals.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef GLOBALS_H
#define GLOBALS_H
#include <vector>
#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <functional>
typedef int nanoseconds_t;
extern double getCurrentTime();
class Error
{
public:
~Error()
{
std::cerr << "Error: " << _stream.str() << std::endl;
exit(1);
}
template <typename T>
Error& operator<<(T&& t)
{
_stream << t;
return *this;
}
private:
std::stringstream _stream;
};
#endif

23
lib/reader.cc Normal file
View File

@@ -0,0 +1,23 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
static StringFlag source(
{ "--source", "-s" },
"source for data",
"");
static StringFlag destination(
{ "--write-flux", "-f" },
"write the raw magnetic flux to this file",
"");
static SettableFlag justRead(
{ "--just-read", "-R" },
"just read the disk but do no further processing");
int allTracks()
{
return 0;
}

6
lib/reader.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef READER_H
#define READER_H
extern int allTracks();
#endif

224
lib/usb.cc Normal file
View File

@@ -0,0 +1,224 @@
#include "globals.h"
#include "usb.h"
#include "protocol.h"
#include <libusb.h>
#define TIMEOUT 5000
static libusb_device_handle* device;
static uint8_t buffer[FRAME_SIZE];
static std::string usberror(int i)
{
return libusb_strerror((libusb_error) i);
}
static void usb_init()
{
if (device)
return;
int i = libusb_init(NULL);
if (i < 0)
Error() << "could not start libusb: " << usberror(i);
device = libusb_open_device_with_vid_pid(NULL, FLUXENGINE_VID, FLUXENGINE_PID);
if (!device)
Error() << "cannot find the FluxEngine (is it plugged in?)";
int cfg = -1;
libusb_get_configuration(device, &cfg);
if (cfg != 1)
{
i = libusb_set_configuration(device, 1);
if (i < 0)
Error() << "the FluxEngine would not accept configuration: " << usberror(i);
}
i = libusb_claim_interface(device, 0);
if (i < 0)
Error() << "could not claim interface: " << usberror(i);
int version = usbGetVersion();
if (version > FLUXENGINE_VERSION)
Error() << "this version of the client is too old for this FluxEngine";
}
static int usb_cmd_send(void* ptr, int len)
{
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_OUT_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to send command: " << usberror(i);
return len;
}
void usb_cmd_recv(void* ptr, int len)
{
int i = libusb_interrupt_transfer(device, FLUXENGINE_CMD_IN_EP,
(uint8_t*) ptr, len, &len, TIMEOUT);
if (i < 0)
Error() << "failed to receive command reply: " << usberror(i);
}
static void bad_reply(void)
{
struct error_frame* f = (struct error_frame*) buffer;
if (f->f.type != F_FRAME_ERROR)
Error() << "bad USB reply %d" << f->f.type;
switch (f->error)
{
case F_ERROR_BAD_COMMAND:
Error() << "device did not understand command";
case F_ERROR_UNDERRUN:
Error() << "USB underrun (not enough bandwidth)";
default:
Error() << "unknown device error " << f->error;
}
}
template <typename T>
static T* await_reply(int desired)
{
usb_cmd_recv(buffer, sizeof(buffer));
struct any_frame* r = (struct any_frame*) buffer;
if (r->f.type != desired)
bad_reply();
return (T*) r;
}
int usbGetVersion(void)
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_GET_VERSION_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct version_frame>(F_FRAME_GET_VERSION_REPLY);
return r->version;
}
void usbSeek(uint8_t track)
{
usb_init();
struct seek_frame f = {
{ .type = F_FRAME_SEEK_CMD, .size = sizeof(f) },
.track = track
};
usb_cmd_send(&f, f.f.size);
await_reply<struct any_frame>(F_FRAME_SEEK_REPLY);
}
nanoseconds_t usbGetRotationalPeriod(void)
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_MEASURE_SPEED_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
auto r = await_reply<struct speed_frame>(F_FRAME_MEASURE_SPEED_REPLY);
return r->period_ms * 1000;
}
static int large_bulk_transfer(int ep, void* buffer, int total_len)
{
int len;
int i = libusb_bulk_transfer(device, ep, (uint8_t*) buffer, total_len, &len, TIMEOUT);
if (i < 0)
Error() << "data transfer failed: " << usberror(i);
return len;
}
void usbTestBulkTransport()
{
usb_init();
struct any_frame f = { .f = {.type = F_FRAME_BULK_TEST_CMD, .size = sizeof(f)} };
usb_cmd_send(&f, f.f.size);
/* These must match the device. */
const int XSIZE = 64;
const int YSIZE = 256;
const int ZSIZE = 64;
uint8_t bulk_buffer[XSIZE*YSIZE*ZSIZE];
int total_len = sizeof(bulk_buffer);
double start_time = getCurrentTime();
large_bulk_transfer(FLUXENGINE_DATA_IN_EP, bulk_buffer, total_len);
double elapsed_time = getCurrentTime() - start_time;
std::cout << "Transferred "
<< total_len
<< " bytes in "
<< int(elapsed_time * 1000.0)
<< " ("
<< int((total_len / 1024.0) / elapsed_time)
<< " kB/s)"
<< std::endl;
for (int x=0; x<XSIZE; x++)
for (int y=0; y<YSIZE; y++)
for (int z=0; z<ZSIZE; z++)
{
int offset = x*XSIZE*YSIZE + y*ZSIZE + z;
if (bulk_buffer[offset] != uint8_t(x+y+z))
Error() << "data transfer corrupted at 0x"
<< std::hex << offset << std::dec
<< " "
<< x << '.' << y << '.' << z << '.';
}
await_reply<struct any_frame>(F_FRAME_BULK_TEST_REPLY);
}
#if 0
struct fluxmap* usb_read(int side, int revolutions)
{
struct read_frame f = {
.f = { .type = F_FRAME_READ_CMD, .size = sizeof(f) },
.side = side,
.revolutions = revolutions
};
struct fluxmap* fluxmap = create_fluxmap();
usb_cmd_send(&f, f.f.size);
uint8_t buffer[1024*1024];
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer, sizeof(buffer));
fluxmap_append_intervals(fluxmap, buffer, len);
await_reply(F_FRAME_READ_REPLY);
return fluxmap;
}
/* Returns number of bytes actually written */
void usb_write(int side, struct fluxmap* fluxmap)
{
int safelen = fluxmap->bytes & ~(FRAME_SIZE-1);
/* Convert from intervals to absolute timestamps. */
uint8_t buffer[1024*1024];
uint8_t clock = 0;
for (int i=0; i<safelen; i++)
{
clock += fluxmap->intervals[i];
buffer[i] = clock;
}
struct write_frame f = {
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
.side = side,
.bytes_to_write = htole32(safelen),
};
usb_cmd_send(&f, f.f.size);
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, buffer, safelen);
await_reply(F_FRAME_WRITE_REPLY);
}
#endif

9
lib/usb.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef USB_H
#define USB_H
extern int usbGetVersion();
extern void usbSeek(uint8_t track);
extern nanoseconds_t usbGetRotationalPeriod();
extern void usbTestBulkTransport();
#endif

View File

11
src/fe-readibm.cc Normal file
View File

@@ -0,0 +1,11 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
int main(int argc, const char* argv[])
{
Flag::parseFlags(argc, argv);
allTracks();
return 0;
}

13
src/fe-rpm.cc Normal file
View File

@@ -0,0 +1,13 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
int main(int argc, const char* argv[])
{
Flag::parseFlags(argc, argv);
nanoseconds_t period = usbGetRotationalPeriod();
std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl;
return 0;
}

16
src/fe-seek.cc Normal file
View File

@@ -0,0 +1,16 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
static IntFlag track(
{ "--track", "-t" },
"track to seek to",
0);
int main(int argc, const char* argv[])
{
Flag::parseFlags(argc, argv);
usbSeek(track);
return 0;
}

View File

@@ -0,0 +1,10 @@
#include "globals.h"
#include "flags.h"
#include "usb.h"
int main(int argc, const char* argv[])
{
Flag::parseFlags(argc, argv);
usbTestBulkTransport();
return 0;
}