use std::error::Error;
use std::fs::File;
use std::env::Args;
use std::io::Write;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use rand::prelude::*;
use rand_pcg::{Pcg64, Lcg128Xsl64};
#[derive(PartialEq)]
pub enum HelpType {NoHelp, Short, Long}
pub struct Config
{
pub seed: u64,
pub num_variants: u32,
pub help: HelpType
}
impl Config
{
pub fn new(args: Args) -> Result<Config, &'static str>
{
let mut seed: u64 = 0;
let mut num_variants: u32 = 0;
let mut help: HelpType = HelpType::NoHelp;
let mut args = args.peekable();
let num_args = args.len();
if num_args < 2
{
help = HelpType::Short;
}
else
{
args.next();
while let Some(arg) = args.next()
{
match arg.as_str()
{
"-h" => help = HelpType::Long,
"-s" => seed = match args.next()
{
Some(arg_string) => match arg_string.parse::<u64>()
{
Ok(num) => num,
Err(_) => return Err("Invalid numeric seed! Use -S for non-numeric seeds."),
},
None => return Err("Please provide a numeric seed."),
},
"-S" => seed = match args.next()
{
Some(arg_string) =>
{
let mut hasher = DefaultHasher::new();
arg_string.hash(&mut hasher);
hasher.finish()
}
None => return Err("Please provide a seed."),
},
_ => if let Some(_) = args.peek()
{
return Err("Invalid option provided! Type vmks-exam-generator -h for help.");
}
else
{
num_variants = match arg.parse::<u32>()
{
Ok(num) => num,
Err(_) => return Err("Invalid number of variants!"),
}
},
}
};
if num_variants == 0 && help == HelpType::NoHelp
{
return Err("Please provide a number of variants greater than 0.");
}
}
Ok(Config
{
seed,
num_variants,
help
})
}
}
trait PushRandom
{
fn push_random(&mut self, list: &[&str], rng: &mut Lcg128Xsl64);
}
impl PushRandom for String
{
fn push_random(&mut self, list: &[&str], rng: &mut Lcg128Xsl64)
{
self.push_str(&list[rng.gen_range(0, list.len())]);
}
}
pub fn run(config: Config) -> Result<(), Box<dyn Error>>
{
match config.help
{
HelpType::Short =>
{
println!("Usage: vmks-exam-generator [OPTIONS] NUM\nType vmks-exam-generator -h for more information.");
Ok(())
},
HelpType::Long =>
{
println!("Usage: vmks-exam-generator [OPTIONS] NUM\n\
Pseudo-randomly generates NUM variants of an embedded programming exam. If no seed is provided, 0 is used as seed.\n\
\t-h\t Show this help\n\
\t-s SEED\t Use the number SEED to seed the random number generator\n\
\t-S SEED\t Use the hashsum of the string SEED to seed the random number generator");
Ok(())
},
HelpType::NoHelp =>
{
let mut rng: Lcg128Xsl64 = Pcg64::seed_from_u64(config.seed);
generate_variants(config.num_variants, &mut rng)
},
}
}
fn generate_variants(num_variants: u32, rng: &mut Lcg128Xsl64) -> Result<(), Box<dyn Error>>
{
const TASKS: [&str; 13] =
[
"Контролно по \"Програмиране на вградени микроконтролерни системи\"\n\nЗадача 1:\nСвържете ",
" между пин ",
" и ",
". При ",
" на веригата да се изпълни прекъсване, което да ",
"\n\nЗадача 2:\nСвържете потенциометър, чрез който да се задава напрежение между 0V и 5V на пин ",
" на Arduino Uno. ",
" на зададеното чрез потенциометъра напрежение.\n\nЗадача 3:\nКонфигурирайте канал ",
" на таймер 1 на Arduino Uno със следните параметри:\n\tчестота - ",
"\n\tкоефициент на запълване - ",
"\n\tрежим 14 - Fast PWM (виж таблица 15-5)\n\t",
" режим на пиновете (виж таблица 15-3)\nКонфигурирайте прекъсване при ",
", което да инкрементира една 16-битова целочислена променлива.\n"
];
let task_variants: [Vec<&str>; 12] =
[
vec!["ключ", "бутон"],
vec!["2", "3"],
vec!["захранване", "земя"],
vec!["затваряне", "отваряне"],
vec!
[
"сменя цвета на 7-мия пиксел на 12-пикселова светодиодна лента между жълто и синьо. Лентата да е свързана към пин 12 на Arduino Uno.",
"сменя цвета на 3-тия пиксел на 10-пикселова светодиодна лента между зелено и бяло. Лентата да е свързана към пин 12 на Arduino Uno.",
"обръща посоката на въртене на постояннотоков четков мотор управляван с H-bridge L293. Сигналите за управление на мотора да са \
свързани към пинове 7 и 8 на Arduino Uno, а разрешаващият вход на L293 да е винаги активен.",
"включва и изключва постояннотоков четков мотор управляван с H-bridge L293. Сигналите за управление на мотора да са \
свързани към пинове 7 и 8 на Arduino Uno, а разрешаващият вход на L293 да е винаги активен.",
"увеличава ъгъла на завъртане на сервомотор с 10 градуса. Когато ъгълът стане по-голям от 120 градуса да се върне на 0 градуса. \
Сервомоторът да е свързан към пин 11 на Arduino Uno.",
"изпраща по серийната връзка времето в милисекунди от стартирането на програмата. Параметрите на серийната връзка да бъдат\n\
\tскорост - 38400 b/s\n\t8 бита за данни\n\tпроверка по четност - EVEN\n\t2 стоп бита",
"изпраща по серийната връзка времето в милисекунди от стартирането на програмата. Параметрите на серийната връзка да бъдат\n\
\tскорост - 57600 b/s\n\t8 бита за данни\n\tпроверка по четност - ODD\n\t1 стоп бит",
],
vec!["A0", "A1", "A2", "A3", "A4", "A5"],
vec!
[
"Към пин 6 на Arduino Uno да се свърже сервомотор. Ъгълът на завъртане на сервомотора да се поддържа правопропорционален",
"Към пин 4 на Arduino Uno да се свърже 20-пикселова светодиодна лента. Да светят в цвят по избор брой пиксели правопропорционален",
"Към пинове 5 и 6 на Arduino Uno да се свържат два от управляващите сигнали на драйвер L293 и съответния разрешаващ вход да се свърже \
към пин 13. Към съответните изходи на драйвера да се свърже постояннотоков четков мотор, чиято скорост да се поддържа правопропорционална",
"Към пин 4 на Arduino Uno да се свърже червен светодиод, който да мига с коефициент на запълване 25% и честота, правопропорционална",
"Към пин 4 на Arduino Uno да се свърже зелен светодиод, който да мига с честота 10 Hz и коефициент на запълване, правопропорционален"
],
vec!["A", "B"],
vec!
[
"1 MHz", "500 kHz", "250 kHz", "100 kHz", "50 kHz", "20 kHz", "10 kHz",
"5 kHz","1 kHz", "500 Hz", "200 Hz", "100 Hz", "50 Hz", "20 Hz"
],
vec!["50%", "25%", "75%", "12.5%", "37.5%", "62.5%", "87.5%"],
vec!["инвертиращ", "неинвертиращ"],
vec!["Compare match", "Overflow"]
];
let mut buf = String::new();
buf.reserve(2000);
for i in 0..num_variants
{
let mut task_variants_iter = task_variants.iter();
for task in TASKS.iter()
{
buf.push_str(task);
if let Some(vars) = task_variants_iter.next()
{
buf.push_random(vars, rng);
};
}
let mut file = File::create(format!("variant_{}.txt", i + 1))?;
file.write_all(buf.as_bytes())?;
buf.truncate(0);
}
Ok(())
}