mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Greying out of the option buttons now works; but the whole way configs are
handled is pretty unsatisfactory and needs work.
This commit is contained in:
@@ -50,7 +50,7 @@ void Config::set(std::string key, std::string value)
|
||||
setProtoByString(*this, key, value);
|
||||
}
|
||||
|
||||
std::string Config::get(std::string key)
|
||||
std::string Config::get(std::string key) const
|
||||
{
|
||||
return getProtoByString(*this, key);
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void Config::readConfigFile(std::string filename)
|
||||
globalConfig()->MergeFrom(loadSingleConfigFile(filename));
|
||||
}
|
||||
|
||||
const OptionProto& Config::findOption(const std::string& optionName)
|
||||
const OptionProto& Config::findOption(const std::string& optionName) const
|
||||
{
|
||||
const OptionProto* found = nullptr;
|
||||
|
||||
@@ -108,10 +108,11 @@ const OptionProto& Config::findOption(const std::string& optionName)
|
||||
return *found;
|
||||
}
|
||||
|
||||
throw OptionNotFoundException("option name not found");
|
||||
throw OptionNotFoundException(
|
||||
fmt::format("option {} not found", optionName));
|
||||
}
|
||||
|
||||
void Config::checkOptionValid(const OptionProto& option)
|
||||
void Config::checkOptionValid(const OptionProto& option) const
|
||||
{
|
||||
for (const auto& req : option.requires())
|
||||
{
|
||||
@@ -151,7 +152,7 @@ void Config::checkOptionValid(const OptionProto& option)
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::isOptionValid(const OptionProto& option)
|
||||
bool Config::isOptionValid(const OptionProto& option) const
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -164,6 +165,11 @@ bool Config::isOptionValid(const OptionProto& option)
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::isOptionValid(std::string option) const
|
||||
{
|
||||
return isOptionValid(findOption(option));
|
||||
}
|
||||
|
||||
void Config::applyOption(const OptionProto& option)
|
||||
{
|
||||
if (option.config().option_size() > 0)
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
/* Set and get individual config keys. */
|
||||
|
||||
void set(std::string key, std::string value);
|
||||
std::string get(std::string key);
|
||||
std::string get(std::string key) const;
|
||||
|
||||
/* Reset the entire configuration. */
|
||||
|
||||
@@ -75,9 +75,10 @@ public:
|
||||
/* Option management: look up an option by name, determine whether an option
|
||||
* is valid, and apply an option. */
|
||||
|
||||
const OptionProto& findOption(const std::string& option);
|
||||
void checkOptionValid(const OptionProto& option);
|
||||
bool isOptionValid(const OptionProto& option);
|
||||
const OptionProto& findOption(const std::string& option) const;
|
||||
void checkOptionValid(const OptionProto& option) const;
|
||||
bool isOptionValid(const OptionProto& option) const;
|
||||
bool isOptionValid(std::string option) const;
|
||||
void applyOption(const OptionProto& option);
|
||||
|
||||
/* Adjust overall inputs and outputs. */
|
||||
|
||||
@@ -2,7 +2,7 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
// Next: 15
|
||||
// Next: 17
|
||||
message DriveProto
|
||||
{
|
||||
optional int32 drive = 1
|
||||
@@ -11,6 +11,9 @@ message DriveProto
|
||||
[ default = INDEXMODE_DRIVE, (help) = "index pulse source" ];
|
||||
optional int32 hard_sector_count = 3
|
||||
[ default = 0, (help) = "number of hard sectors on disk" ];
|
||||
optional double hard_sector_threshold_ns = 16
|
||||
[ default = 0, (help) = "index pulses longer than this interval are "
|
||||
"considered sector markers; shorter indicates an true index marker" ];
|
||||
optional bool high_density = 4
|
||||
[ default = true, (help) = "set if this is a high density disk" ];
|
||||
optional bool sync_with_index = 5
|
||||
|
||||
@@ -11,32 +11,19 @@
|
||||
class HardwareFluxSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& conf): _config(conf)
|
||||
{
|
||||
nanoseconds_t oneRevolution;
|
||||
measureDiskRotation(oneRevolution, _hardSectorThreshold);
|
||||
}
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& conf): _config(conf) {}
|
||||
|
||||
~HardwareFluxSink() {}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
|
||||
{
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
globalConfig()->drive().high_density(),
|
||||
globalConfig()->drive().index_mode());
|
||||
#if 0
|
||||
if (fluxSourceSinkFortyTrack)
|
||||
{
|
||||
if (track & 1)
|
||||
error("cannot write to odd physical tracks in 40-track mode");
|
||||
usbSeek(track / 2);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
auto& drive = globalConfig()->drive();
|
||||
usbSetDrive(drive.drive(), drive.high_density(), drive.index_mode());
|
||||
usbSeek(track);
|
||||
|
||||
return usbWrite(side, fluxmap.rawBytes(), _hardSectorThreshold);
|
||||
return usbWrite(
|
||||
side, fluxmap.rawBytes(), drive.hard_sector_threshold_ns());
|
||||
}
|
||||
|
||||
bool isHardware() const override
|
||||
@@ -51,7 +38,6 @@ public:
|
||||
|
||||
private:
|
||||
const HardwareFluxSinkProto& _config;
|
||||
nanoseconds_t _hardSectorThreshold;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(
|
||||
|
||||
@@ -14,54 +14,48 @@ private:
|
||||
class HardwareFluxSourceIterator : public FluxSourceIterator
|
||||
{
|
||||
public:
|
||||
HardwareFluxSourceIterator(
|
||||
const HardwareFluxSource& fluxsource, int track, int head):
|
||||
_fluxsource(fluxsource),
|
||||
HardwareFluxSourceIterator(int track, int head):
|
||||
_track(track),
|
||||
_head(head)
|
||||
{
|
||||
}
|
||||
|
||||
bool hasNext() const
|
||||
bool hasNext() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<const Fluxmap> next()
|
||||
std::unique_ptr<const Fluxmap> next() override
|
||||
{
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
globalConfig()->drive().high_density(),
|
||||
globalConfig()->drive().index_mode());
|
||||
const auto& drive = globalConfig()->drive();
|
||||
|
||||
usbSetDrive(
|
||||
drive.drive(), drive.high_density(), drive.index_mode());
|
||||
usbSeek(_track);
|
||||
|
||||
Bytes data = usbRead(_head,
|
||||
globalConfig()->drive().sync_with_index(),
|
||||
globalConfig()->drive().revolutions() *
|
||||
_fluxsource._oneRevolution,
|
||||
_fluxsource._hardSectorThreshold);
|
||||
drive.sync_with_index(),
|
||||
drive.revolutions() * drive.rotational_period_ms() * 1e6,
|
||||
drive.hard_sector_threshold_ns());
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBytes(data);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const HardwareFluxSource& _fluxsource;
|
||||
int _track;
|
||||
int _head;
|
||||
};
|
||||
|
||||
public:
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
||||
{
|
||||
measureDiskRotation(_oneRevolution, _hardSectorThreshold);
|
||||
}
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf) {}
|
||||
|
||||
~HardwareFluxSource() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<FluxSourceIterator> readFlux(int track, int head) override
|
||||
{
|
||||
return std::make_unique<HardwareFluxSourceIterator>(*this, track, head);
|
||||
return std::make_unique<HardwareFluxSourceIterator>(track, head);
|
||||
}
|
||||
|
||||
void recalibrate() override
|
||||
@@ -81,8 +75,7 @@ public:
|
||||
|
||||
private:
|
||||
const HardwareFluxSourceProto& _config;
|
||||
nanoseconds_t _oneRevolution;
|
||||
nanoseconds_t _hardSectorThreshold;
|
||||
bool _measured;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(
|
||||
|
||||
@@ -55,33 +55,24 @@ private:
|
||||
_cache;
|
||||
};
|
||||
|
||||
void measureDiskRotation(
|
||||
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold)
|
||||
void measureDiskRotation()
|
||||
{
|
||||
log(BeginSpeedOperationLogMessage());
|
||||
|
||||
int retries = 5;
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
globalConfig()->drive().high_density(),
|
||||
globalConfig()->drive().index_mode());
|
||||
oneRevolution = globalConfig()->drive().rotational_period_ms() * 1e6;
|
||||
if (globalConfig()->drive().hard_sector_count() != 0)
|
||||
hardSectorThreshold = oneRevolution * 3 /
|
||||
(4 * globalConfig()->drive().hard_sector_count());
|
||||
else
|
||||
hardSectorThreshold = 0;
|
||||
|
||||
nanoseconds_t oneRevolution =
|
||||
globalConfig()->drive().rotational_period_ms() * 1e6;
|
||||
if (oneRevolution == 0)
|
||||
{
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
globalConfig()->drive().high_density(),
|
||||
globalConfig()->drive().index_mode());
|
||||
|
||||
log(BeginOperationLogMessage{"Measuring drive rotational speed"});
|
||||
int retries = 5;
|
||||
do
|
||||
{
|
||||
oneRevolution = usbGetRotationalPeriod(
|
||||
globalConfig()->drive().hard_sector_count());
|
||||
if (globalConfig()->drive().hard_sector_count() != 0)
|
||||
hardSectorThreshold =
|
||||
oneRevolution * 3 /
|
||||
(4 * globalConfig()->drive().hard_sector_count());
|
||||
|
||||
retries--;
|
||||
} while ((oneRevolution == 0) && (retries > 0));
|
||||
@@ -90,6 +81,13 @@ void measureDiskRotation(
|
||||
log(EndOperationLogMessage{});
|
||||
}
|
||||
|
||||
if (!globalConfig()->drive().hard_sector_threshold_ns())
|
||||
{
|
||||
globalConfig()->mutable_drive()->set_hard_sector_threshold_ns(
|
||||
oneRevolution * 3 /
|
||||
(4 * globalConfig()->drive().hard_sector_count()));
|
||||
}
|
||||
|
||||
if (oneRevolution == 0)
|
||||
error("Failed\nIs a disk in the drive?");
|
||||
|
||||
@@ -258,6 +256,8 @@ void writeTracks(FluxSink& fluxSink,
|
||||
{
|
||||
log(BeginOperationLogMessage{"Encoding and writing to disk"});
|
||||
|
||||
if (fluxSink.isHardware())
|
||||
measureDiskRotation();
|
||||
int index = 0;
|
||||
for (auto& trackInfo : trackInfos)
|
||||
{
|
||||
@@ -495,6 +495,8 @@ std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||
auto diskflux = std::make_shared<DiskFlux>();
|
||||
|
||||
log(BeginOperationLogMessage{"Reading and decoding disk"});
|
||||
if (fluxSource.isHardware())
|
||||
measureDiskRotation();
|
||||
auto locations = Layout::computeLocations();
|
||||
unsigned index = 0;
|
||||
for (auto& trackInfo : locations)
|
||||
@@ -605,6 +607,8 @@ void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
|
||||
{
|
||||
log(BeginOperationLogMessage{"Performing raw read of disk"});
|
||||
|
||||
if (fluxsource.isHardware() || fluxsink.isHardware())
|
||||
measureDiskRotation();
|
||||
auto locations = Layout::computeLocations();
|
||||
unsigned index = 0;
|
||||
for (auto& trackInfo : locations)
|
||||
|
||||
@@ -14,8 +14,7 @@ class ImageWriter;
|
||||
class TrackInfo;
|
||||
class TrackFlux;
|
||||
|
||||
extern void measureDiskRotation(
|
||||
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold);
|
||||
extern void measureDiskRotation();
|
||||
|
||||
extern void writeTracks(FluxSink& fluxSink,
|
||||
const std::function<std::unique_ptr<const Fluxmap>(
|
||||
|
||||
@@ -71,6 +71,11 @@ option_group {
|
||||
comment: '140kB 5.25" 35-track SS'
|
||||
set_by_default: true
|
||||
|
||||
requires {
|
||||
key: "drive.tpi"
|
||||
value: ["48", "96"]
|
||||
}
|
||||
|
||||
config {
|
||||
layout {
|
||||
tpi: 48
|
||||
@@ -91,6 +96,11 @@ option_group {
|
||||
name: "640"
|
||||
comment: '640kB 5.25" 80-track DS'
|
||||
|
||||
requires {
|
||||
key: "drive.tpi"
|
||||
value: ["96"]
|
||||
}
|
||||
|
||||
config {
|
||||
layout {
|
||||
tpi: 96
|
||||
|
||||
@@ -206,26 +206,6 @@ public:
|
||||
globalConfig().readConfigFile(setting);
|
||||
}
|
||||
|
||||
/* Apply any format options. */
|
||||
|
||||
std::set<std::string> options;
|
||||
for (const auto& e : _formatOptions)
|
||||
{
|
||||
if (e.first == formatName)
|
||||
options.insert(e.second);
|
||||
}
|
||||
|
||||
/* Locate the device, if any. */
|
||||
|
||||
auto serial = _selectedDevice;
|
||||
if (!serial.empty() && (serial[0] == '/'))
|
||||
overrides.push_back(
|
||||
std::make_pair("usb.greaseweazle.port", serial));
|
||||
else
|
||||
overrides.push_back(std::make_pair("usb.serial", serial));
|
||||
|
||||
ClearLog();
|
||||
|
||||
/* Apply the source/destination. */
|
||||
|
||||
switch (_selectedSource)
|
||||
@@ -240,7 +220,6 @@ public:
|
||||
globalConfig().setFluxSink(filename);
|
||||
globalConfig().setFluxSource(filename);
|
||||
globalConfig().setVerificationFluxSource(filename);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -259,6 +238,35 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply any format options. */
|
||||
|
||||
std::set<std::string> options;
|
||||
for (const auto& e : _formatOptions)
|
||||
{
|
||||
if (e.first == formatName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (globalConfig().isOptionValid(e.second))
|
||||
options.insert(e.second);
|
||||
}
|
||||
catch (const OptionException& e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Locate the device, if any. */
|
||||
|
||||
auto serial = _selectedDevice;
|
||||
if (!serial.empty() && (serial[0] == '/'))
|
||||
overrides.push_back(
|
||||
std::make_pair("usb.greaseweazle.port", serial));
|
||||
else
|
||||
overrides.push_back(std::make_pair("usb.serial", serial));
|
||||
|
||||
ClearLog();
|
||||
|
||||
/* Resolve the rest of the stuff. */
|
||||
|
||||
globalConfig().initialise(options, overrides);
|
||||
@@ -574,111 +582,107 @@ private:
|
||||
|
||||
void UpdateFormatOptions()
|
||||
{
|
||||
int formatSelection = formatChoice->GetSelection();
|
||||
_currentlyDisplayedFormat = formatSelection;
|
||||
|
||||
PrepareConfig();
|
||||
assert(!wxGetApp().IsWorkerThreadRunning());
|
||||
|
||||
int formatSelection = formatChoice->GetSelection();
|
||||
if (formatSelection != _currentlyDisplayedFormat)
|
||||
formatOptionsContainer->DestroyChildren();
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
if (formatSelection == wxNOT_FOUND)
|
||||
sizer->Add(new wxStaticText(
|
||||
formatOptionsContainer, wxID_ANY, "(no format selected)"));
|
||||
else
|
||||
{
|
||||
_currentlyDisplayedFormat = formatSelection;
|
||||
formatOptionsContainer->DestroyChildren();
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
std::string formatName = _formatNames[formatChoice->GetSelection()];
|
||||
|
||||
if (formatSelection == wxNOT_FOUND)
|
||||
sizer->Add(new wxStaticText(
|
||||
formatOptionsContainer, wxID_ANY, "(no format selected)"));
|
||||
else
|
||||
for (auto& group : globalConfig()->option_group())
|
||||
{
|
||||
globalConfig().clear();
|
||||
std::string formatName =
|
||||
_formatNames[formatChoice->GetSelection()];
|
||||
globalConfig().readConfigFile(formatName);
|
||||
sizer->Add(new wxStaticText(
|
||||
formatOptionsContainer, wxID_ANY, group.comment() + ":"));
|
||||
|
||||
for (auto& group : globalConfig()->option_group())
|
||||
bool first = true;
|
||||
bool valueSet = false;
|
||||
wxRadioButton* defaultButton = nullptr;
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
sizer->Add(new wxStaticText(formatOptionsContainer,
|
||||
auto* rb = new wxRadioButton(formatOptionsContainer,
|
||||
wxID_ANY,
|
||||
group.comment() + ":"));
|
||||
|
||||
bool first = true;
|
||||
bool valueSet = false;
|
||||
wxRadioButton* defaultButton = nullptr;
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
auto* rb = new wxRadioButton(formatOptionsContainer,
|
||||
wxID_ANY,
|
||||
option.comment(),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
first ? wxRB_GROUP : 0);
|
||||
auto key = std::make_pair(formatName, option.name());
|
||||
sizer->Add(rb);
|
||||
|
||||
rb->Bind(wxEVT_RADIOBUTTON,
|
||||
[=](wxCommandEvent& e)
|
||||
{
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
_formatOptions.erase(std::make_pair(
|
||||
formatName, option.name()));
|
||||
}
|
||||
|
||||
_formatOptions.insert(key);
|
||||
|
||||
OnControlsChanged(e);
|
||||
});
|
||||
|
||||
if (_formatOptions.find(key) != _formatOptions.end())
|
||||
{
|
||||
rb->SetValue(true);
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
if (option.set_by_default() || !defaultButton)
|
||||
defaultButton = rb;
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!valueSet && defaultButton)
|
||||
defaultButton->SetValue(true);
|
||||
}
|
||||
|
||||
/* Anything that's _not_ in a group gets a checkbox. */
|
||||
|
||||
for (auto& option : globalConfig()->option())
|
||||
{
|
||||
auto* choice = new wxCheckBox(
|
||||
formatOptionsContainer, wxID_ANY, option.comment());
|
||||
option.comment(),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
first ? wxRB_GROUP : 0);
|
||||
auto key = std::make_pair(formatName, option.name());
|
||||
sizer->Add(choice);
|
||||
sizer->Add(rb);
|
||||
|
||||
choice->SetValue(
|
||||
(_formatOptions.find(key) != _formatOptions.end()) ||
|
||||
option.set_by_default());
|
||||
|
||||
choice->Bind(wxEVT_CHECKBOX,
|
||||
rb->Bind(wxEVT_RADIOBUTTON,
|
||||
[=](wxCommandEvent& e)
|
||||
{
|
||||
if (choice->GetValue())
|
||||
_formatOptions.insert(key);
|
||||
else
|
||||
_formatOptions.erase(key);
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
_formatOptions.erase(
|
||||
std::make_pair(formatName, option.name()));
|
||||
}
|
||||
|
||||
_formatOptions.insert(key);
|
||||
|
||||
OnControlsChanged(e);
|
||||
});
|
||||
|
||||
if (_formatOptions.find(key) != _formatOptions.end())
|
||||
{
|
||||
rb->SetValue(true);
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
rb->Enable(globalConfig().isOptionValid(option));
|
||||
if (option.set_by_default() || !defaultButton)
|
||||
defaultButton = rb;
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (globalConfig()->option().empty() &&
|
||||
globalConfig()->option_group().empty())
|
||||
sizer->Add(new wxStaticText(formatOptionsContainer,
|
||||
wxID_ANY,
|
||||
"(no options for this format)"));
|
||||
if (!valueSet && defaultButton)
|
||||
defaultButton->SetValue(true);
|
||||
}
|
||||
|
||||
formatOptionsContainer->SetSizerAndFit(sizer);
|
||||
Layout();
|
||||
SafeFit();
|
||||
/* Anything that's _not_ in a group gets a checkbox. */
|
||||
|
||||
for (auto& option : globalConfig()->option())
|
||||
{
|
||||
auto* choice = new wxCheckBox(
|
||||
formatOptionsContainer, wxID_ANY, option.comment());
|
||||
auto key = std::make_pair(formatName, option.name());
|
||||
sizer->Add(choice);
|
||||
|
||||
choice->SetValue(
|
||||
(_formatOptions.find(key) != _formatOptions.end()) ||
|
||||
option.set_by_default());
|
||||
|
||||
choice->Bind(wxEVT_CHECKBOX,
|
||||
[=](wxCommandEvent& e)
|
||||
{
|
||||
if (choice->GetValue())
|
||||
_formatOptions.insert(key);
|
||||
else
|
||||
_formatOptions.erase(key);
|
||||
|
||||
OnControlsChanged(e);
|
||||
});
|
||||
}
|
||||
|
||||
if (globalConfig()->option().empty() &&
|
||||
globalConfig()->option_group().empty())
|
||||
sizer->Add(new wxStaticText(formatOptionsContainer,
|
||||
wxID_ANY,
|
||||
"(no options for this format)"));
|
||||
}
|
||||
|
||||
formatOptionsContainer->SetSizerAndFit(sizer);
|
||||
Layout();
|
||||
SafeFit();
|
||||
}
|
||||
|
||||
void UpdateDevices()
|
||||
|
||||
@@ -74,6 +74,14 @@ static void test_option_validity()
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
test_option_validity();
|
||||
return 0;
|
||||
try
|
||||
{
|
||||
test_option_validity();
|
||||
return 0;
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
fmt::print(stderr, "Error: {}\n", e.message);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user