/* This file is part of LED Pattern Editor.
*
* Copyright (C) 2010 Philipp Zabel
*
* LED Pattern Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LED Pattern Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LED Pattern Editor. If not, see .
*/
class LedPatternRX44 : LedPattern {
public List engine_r;
public List engine_g;
public List engine_b;
public override void parse (string line) throws LedPatternError {
string[] key_value = line.split ("=");
if (key_value.length != 2)
throw new LedPatternError.INVALID_PATTERN ("pattern line does not contain '=': " + line);
name = key_value[0];
string[] p = key_value[1].split (";");
if (p.length != 6)
throw new LedPatternError.INVALID_PATTERN ("%s does not contain 6 components: %d".printf (name, p.length));
if (p[3].length > 16*4 || p[4].length > 16*4 || p[5].length > 16*4)
throw new LedPatternError.INVALID_PATTERN ("%s engine pattern too long!".printf (name));
if (p[3].length % 4 != 0 || p[4].length % 4 != 0 || p[5].length % 4 != 0)
throw new LedPatternError.INVALID_PATTERN ("%s engine pattern not an even number of bytes!".printf (name));
priority = p[0].to_int ();
screen_on = p[1].to_int ();
timeout = p[2].to_int ();
engine_r = parse_pattern (p[3]);
engine_g = parse_pattern (p[4]);
engine_b = parse_pattern (p[5]);
on_changed ();
}
private List parse_pattern (string pattern) {
var list = new List ();
var length = pattern.length;
if (length % 4 != 0)
return list;
char *p = ((char*) pattern) + length - 4;
while (p >= (char*) pattern) {
var command = new LedCommandRX44.with_code ((uint16) ((string) p).to_ulong (null, 16));
command.changed.connect (on_changed);
list.prepend (command);
p[0] = '\0';
p -= 4;
}
return list;
}
public override string dump () {
return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout,
dump_pattern (engine_r), dump_pattern (engine_g), dump_pattern (engine_b));
}
private string dump_pattern (List list) {
string result = "";
foreach (LedCommandRX44 command in list) {
result += "%04x".printf (command.code);
}
return result;
}
public LedPatternRX44 copy () {
var pattern = new LedPatternRX44 ();
pattern.name = name.dup ();
pattern.priority = priority;
pattern.screen_on = screen_on;
pattern.timeout = timeout;
pattern.duration = duration;
pattern.engine_r = deep_copy (pattern, engine_r);
pattern.engine_g = deep_copy (pattern, engine_g);
pattern.engine_b = deep_copy (pattern, engine_b);
return pattern;
}
public List deep_copy (LedPatternRX44 pattern, List list) {
var list2 = new List ();
foreach (LedCommandRX44 command in list) {
var command2 = command.copy ();
command2.changed.connect (pattern.on_changed);
list2.append (command2);
}
return list2;
}
public void replace_with (LedPatternRX44 pattern) {
name = pattern.name;
priority = pattern.priority;
screen_on = pattern.screen_on;
timeout = pattern.timeout;
duration = pattern.duration;
engine_r = deep_copy (this, pattern.engine_r);
engine_g = deep_copy (this, pattern.engine_g);
engine_b = deep_copy (this, pattern.engine_b);
changed ();
}
public void on_changed () {
bool unresolved = calculate_timing ();
if (unresolved)
unresolved = calculate_timing ();
if (unresolved)
Hildon.Banner.show_information (null, null, "Timing unresolved");
changed ();
}
private bool calculate_timing () {
bool unresolved = false;
// Calculate timing and level info for red engine
double time = 0;
int level = 0;
foreach (LedCommandRX44 command in engine_r) {
command.time = time;
if (command.type == CommandType.SET_PWM) {
level = command.level;
} else {
command.level = level;
level += command.steps;
}
if (command.type == CommandType.TRIGGER &&
(command.code & 0x0180) != 0) {
command.duration = wait_for_trigger (time, engine_g);
if (command.duration == 0)
unresolved = true;
command.duration += 16 * LedCommandRX44.CYCLE_TIME_MS;
}
time += command.duration;
if (level < 0)
level = 0;
if (level > 255)
level = 255;
}
duration = time;
// Calculate timing and level info for green engine
time = 0;
level = 0;
foreach (LedCommandRX44 command in engine_g) {
command.time = time;
if (command.type == CommandType.SET_PWM) {
level = command.level;
} else {
command.level = level;
level += command.steps;
}
if (command.type == CommandType.TRIGGER &&
(command.code & 0x0180) != 0) {
command.duration = wait_for_trigger (time, engine_r);
if (command.duration == 0)
unresolved = true;
command.duration += 16 * LedCommandRX44.CYCLE_TIME_MS;
}
time += command.duration;
if (level < 0)
level = 0;
if (level > 255)
level = 255;
}
if (time > duration)
duration = time;
return unresolved;
}
double wait_for_trigger (double time, List engine) {
double duration = 0;
bool repeat = false;
foreach (LedCommandRX44 command in engine) {
duration = command.time + command.duration;
if (command.type == CommandType.TRIGGER &&
(command.code & 0x0006) != 0 && command.time > time) {
return command.time - time;
}
if (command.type == CommandType.GO_TO_START) {
repeat = true;
break;
}
}
if (repeat) foreach (LedCommandRX44 command in engine) {
if (command.type == CommandType.TRIGGER &&
(command.code & 0x0006) != 0 && (duration + command.time) > time) {
return duration + command.time - time;
}
}
return 0;
}
}
class LedCommandRX44 : LedCommand {
internal const double CYCLE_TIME_MS = 1000.0 / 32768.0;
public uint16 code;
public LedCommandRX44 () {
}
public LedCommandRX44.with_code (uint16 _code) {
code = _code;
duration = 16 * CYCLE_TIME_MS;
if ((code & 0x8000) == 0) {
if (code == 0x0000) {
type = CommandType.GO_TO_START;
} else if ((code & 0x3e00) != 0) {
type = CommandType.RAMP_WAIT;
steps = code & 0x7f;
step_time = code >> 8;
if ((code & 0x4000) == 0)
step_time = (code >> 9) * 16 * CYCLE_TIME_MS;
else {
step_time = ((code & 0x3e00) >> 9) * 512 * CYCLE_TIME_MS;
}
duration = step_time * (steps + 1);
if ((code & 0x80) != 0)
steps = -steps;
} else {
type = CommandType.SET_PWM;
level = code & 0xff;
}
} else {
if ((code & ~0x1f8f) == 0xa000) {
type = CommandType.BRANCH;
// 0x1f80: (loop count - 1) << 7
// 0x000f: step number
} else if ((code & ~0x1800) == 0xc000) {
type = CommandType.END;
// 0x1000: interrupt
if ((code & 0x0800) != 0) // Reset
steps = -255;
} else if ((code & ~ 0x13fe) == 0xe000) {
type = CommandType.TRIGGER;
// 0x1000: wait ext
// 0x0380: wait B G R
// 0x0040: set ext
// 0x000e: set B G R
}
}
}
public override void set_pwm (int _level) {
code = 0x4000 | _level;
base.set_pwm (_level);
}
public override void ramp_wait (double _step_time, int _steps) requires (_step_time >= (16 * CYCLE_TIME_MS)) {
int step_time;
if (_step_time < 32 * (16 * CYCLE_TIME_MS)) {
step_time = (int) ((_step_time + 0.001) / (16 * CYCLE_TIME_MS));
code = (uint16) step_time << 8;
_step_time = step_time * (16 * CYCLE_TIME_MS);
} else if (_step_time < 32*(512 * CYCLE_TIME_MS)) {
step_time = (int) ((_step_time + 0.001) / (512 * CYCLE_TIME_MS));
code = 0x4000 | (step_time << 8);
_step_time = step_time * (512 * CYCLE_TIME_MS);
} else {
return;
}
if (_steps < 0) {
code |= 0x80 | (-_steps);
} else {
code |= _steps;
}
base.ramp_wait (_step_time, _steps);
}
public override void go_to_start () {
code = 0x0000;
base.go_to_start ();
}
public override void end (bool reset) {
code = 0xc000;
if (reset)
code |= 0x0800;
base.end (reset);
}
public LedCommandRX44 copy () {
var command = new LedCommandRX44 ();
command.type = type;
command.time = time;
command.step_time = step_time;
command.duration = duration;
command.level = level;
command.steps = steps;
command.code = code;
return command;
}
}