public virtual Reply GetReply() { Reply r = new Reply(); r.t = ReplyType.ReplyNone; return r; }
private void Worker() { Action<Reply> SendReply = (Reply r) => { lock (this) { this.replies.AddLast(r); } }; Action CloseSerial = () => { try { if ((this.serialHandle != null) && this.serialHandle.IsOpen) { this.serialHandle.Close(); } } catch (Exception) { } this.serialHandle = null; }; Action<string> ConnectionError = (string s) => { CloseSerial(); Reply r = new Reply(); r.t = ReplyType.ReplyError; r.errorCode = s; SendReply(r); }; Action<byte[], int> SendSerialCommand = (byte[] data, int size) => { if (this.serialHandle == null) { return; } try { this.serialHandle.Write(data, 0, size); int ok = this.serialHandle.ReadByte(); if (ok != 'K') { ConnectionError("Protocol error when sending command " + data[0] + ", got " + ok); } } catch (System.TimeoutException) { ConnectionError("Timeout error when sending command " + data[0]); } catch (Exception e) { ConnectionError("Communications error when sending command " + data[0] + ": " + e.Message); } }; Action<byte[], int> ReadSerialBytes = (byte[] data, int size) => { if (this.serialHandle == null) { return; } try { int count = 0, offset = 0; while (count < size) { int now = this.serialHandle.Read(data, offset, size - count); if (now <= 0) { ConnectionError("Protocol error when reading " + size + " bytes: got " + count + " and " + now); } count += now; offset += now; } } catch (System.TimeoutException) { ConnectionError("Timeout error when reading " + size + " bytes"); } catch (Exception e) { ConnectionError("Communications error when reading " + size + " bytes: " + e.Message); } }; Action<int> SendDisplay = (int d) => { byte[] b = new byte[2]; b[0] = (byte)'D'; b[1] = (byte)d; SendSerialCommand(b, 2); }; Action PostConnection = () => { byte[] bytesOut = new byte[16]; byte[] bytesIn = new byte[16]; // Activities carried out when a connection is established // set display to 0xc SendDisplay(0x10); SendDisplay(0xc); // request current colour Reply r = new Reply(); bytesOut[0] = (byte)'c'; // request current colour SendSerialCommand(bytesOut, 1); if (this.serialHandle == null) return; ReadSerialBytes(bytesIn, 3); if (this.serialHandle == null) return; // Send "connected" message as connection seems ok r.red = (int)bytesIn[0]; r.green = (int)bytesIn[1]; r.blue = (int)bytesIn[2]; r.t = ReplyType.ReplyConnected; SendReply(r); // Download programs for (int i = 0; i < num_programs; i++) { bytesOut[0] = (byte)'L'; // load program from EEPROM bytesOut[1] = (byte)i; SendSerialCommand(bytesOut, 2); if (this.serialHandle == null) return; bytesOut[0] = (byte)'m'; // download program via serial line SendSerialCommand(bytesOut, 1); if (this.serialHandle == null) return; r.program_bytes = new byte[program_size]; r.program_number = i; ReadSerialBytes(r.program_bytes, program_size); if (this.serialHandle == null) return; r.t = ReplyType.ReplyProgram; SendReply(r); } }; Command colour = new Command(); LinkedList<Command> sequential = new LinkedList<Command>(); bool get_colour = false; while (true) { colour.t = CommandType.CommandNone; get_colour = false; sequential.Clear(); // Await messages lock (this) { if (commands.Count == 0) { try { Monitor.Wait(this); } catch (Exception) { } } while (commands.Count != 0) { Command cmd = commands.First(); commands.RemoveFirst(); switch (cmd.t) { case CommandType.CommandSetColour: // Only keep the most recent colour command colour = cmd; break; case CommandType.CommandGetColour: // Done after SetColour get_colour = true; break; case CommandType.CommandExit: // exit takes priority over everything else: immediate exit CloseSerial(); return; default: // All other commands are processed in sequence sequential.AddLast (cmd); break; } } } // Process commands apart from colour while (sequential.Count != 0) { Command cmd = sequential.First(); sequential.RemoveFirst(); switch (cmd.t) { case CommandType.CommandConnect: CloseSerial(); try { this.serialHandle = new SerialPort(cmd.portName, 19200); this.serialHandle.ReadTimeout = timeout; this.serialHandle.WriteTimeout = timeout; this.serialHandle.Open(); } catch (Exception e) { ConnectionError("Connection to " + cmd.portName + " failed with error: " + e.Message); } if (this.serialHandle != null) { // send some '\0' bytes before starting it const int leadin_size = 6; byte[] initBytesOut = new byte[leadin_size]; initBytesOut[leadin_size - 1] = (byte)'Q'; int attempts = 3; while (attempts > 0) { try { this.serialHandle.Write(initBytesOut, 0, leadin_size); } catch (Exception) { ConnectionError("Write error during setup"); break; } try { this.serialHandle.ReadTo("K"); try { // This read is expected to timeout. // Just flushes extra stuff on the input this.serialHandle.ReadTo("Z"); } catch (Exception) { } break; } catch (TimeoutException) { // try again unless final attempt attempts--; if (attempts <= 0) { ConnectionError("Timeout during connection: no valid response"); break; } } catch (Exception) { ConnectionError("Read error during setup"); break; } } PostConnection(); } else { CloseSerial(); } break; case CommandType.CommandSetDisplay: { Reply r = new Reply(); r.t = ReplyType.ReplyMsg; r.errorCode = "Setting display " + cmd.value; SendReply(r); SendDisplay(cmd.value); } break; case CommandType.CommandRunEEPROMProgram: { byte[] bytesOut = new byte[2]; bytesOut[0] = (byte)'L'; // load from EEPROM bytesOut[1] = (byte)cmd.value; // program number SendSerialCommand(bytesOut, 2); bytesOut[0] = (byte)'R'; // run SendSerialCommand(bytesOut, 1); Reply r = new Reply(); r.t = ReplyType.ReplyMsg; r.errorCode = "Running program " + cmd.value; SendReply(r); } break; case CommandType.CommandRunTemporaryProgram: { byte[] bytesOut = new byte[Comms.program_size + 1]; bytesOut[0] = (byte)'M'; // upload program via serial line (PC to Arduino) for (int i = 0; i < Comms.program_size; i++) { bytesOut[i + 1] = cmd.program_bytes[i]; } SendSerialCommand(bytesOut, Comms.program_size + 1); bytesOut[0] = (byte)'R'; // run SendSerialCommand(bytesOut, 1); Reply r = new Reply(); r.t = ReplyType.ReplyMsg; r.errorCode = "Running program from editor"; SendReply(r); } break; case CommandType.CommandSaveEEPROMProgram: { Reply r = new Reply(); r.t = ReplyType.ReplyMsg; r.errorCode = "Writing program " + cmd.program_number + " to EEPROM"; SendReply(r); byte[] bytesOut = new byte[Comms.program_size + 1]; bytesOut[0] = (byte)'M'; // upload program via serial line (PC to Arduino) for (int i = 0; i < Comms.program_size; i++) { bytesOut[i + 1] = cmd.program_bytes[i]; } SendSerialCommand(bytesOut, Comms.program_size + 1); bytesOut[0] = (byte)'S'; // save to EEPROM bytesOut[1] = (byte)cmd.program_number; if (this.serialHandle != null) { this.serialHandle.ReadTimeout = eeprom_write_timeout; SendSerialCommand(bytesOut, 2); } if (this.serialHandle != null) { this.serialHandle.ReadTimeout = timeout; } bytesOut[0] = (byte)'L'; // load program from EEPROM (readback) bytesOut[1] = (byte)cmd.program_number; SendSerialCommand(bytesOut, 2); bytesOut[0] = (byte)'m'; // download program via serial line (readback) SendSerialCommand(bytesOut, 1); if (this.serialHandle != null) { r = new Reply(); r.program_bytes = new byte[Comms.program_size]; r.program_number = cmd.program_number; ReadSerialBytes(r.program_bytes, Comms.program_size); r.t = ReplyType.ReplyProgram; SendReply(r); } if (this.serialHandle != null) { r = new Reply(); r.t = ReplyType.ReplyMsg; r.errorCode = "Done"; SendReply(r); } } break; default: break; } } // Process colour command if (colour.t != CommandType.CommandNone) { byte[] b = new byte[4]; b[0] = (byte) 'C'; b[1] = (byte) colour.red; b[2] = (byte) colour.green; b[3] = (byte) colour.blue; SendSerialCommand (b, 4); } if (get_colour) { get_colour = false; byte[] bytesOut = new byte[1]; bytesOut[0] = (byte)'c'; // request current colour SendSerialCommand(bytesOut, 1); byte[] bytesIn = new byte[3]; ReadSerialBytes(bytesIn, 3); Reply r = new Reply(); r.t = ReplyType.ReplyProgram; r.red = (int)bytesIn[0]; r.green = (int)bytesIn[1]; r.blue = (int)bytesIn[2]; r.t = ReplyType.ReplyGotColour; SendReply(r); } } }
public override Reply GetReply() { Reply r; lock (this) { if (this.replies.Count != 0) { r = this.replies.First(); this.replies.RemoveFirst(); } else { r = new Reply(); r.t = ReplyType.ReplyNone; } } return r; }
public void SimulatorSendReply(Reply r) { this.replies.AddLast(r); }
public void RefreshSimulation(SimulatorComms comms) { if (this.simulated_program != null) { long ticks = DateTime.Now.Ticks - this.start_time; long milliseconds = ticks / TimeSpan.TicksPerMillisecond; int sim_time = (int)milliseconds; int size = this.simulated_program.contents.Count(); if ((this.program_time + 120000) < sim_time) { // clock skew - reset the program this.program_time = 0; this.start_time = DateTime.Now.Ticks; this.program_counter = 0; } while (this.program_time < sim_time) { // run forwards until the program time matches the simulator time if (this.program_counter >= size) { this.program_counter = 0; // loop at end of program } Instruction inst = this.simulated_program.contents[this.program_counter]; int end_time = inst.getTime() + this.program_time; bool commit = true; switch (inst.t) { case InstructionType.InstructionSetDisplay: this.setDisplay(inst.value); break; case InstructionType.InstructionTransition: if (end_time < sim_time) { // Running behind. Apply this instruction immediately. this.r = inst.r; this.g = inst.g; this.b = inst.b; this.setColour(this.r, this.g, this.b); } else { // Transition int rS = this.r, gS = this.g, bS = this.b; int rT = inst.r, gT = inst.g, bT = inst.b; int tD = (int)(sim_time - this.program_time); int tI = (int)(end_time - this.program_time); byte r = linear(rS, rT, tD, tI); byte g = linear(gS, gT, tD, tI); byte b = linear(bS, bT, tD, tI); this.setColour(r, g, b); commit = false; } break; default: break; } if (commit) { // Go to next instruction this.program_counter++; this.program_time = end_time; } else { // Need to wait for this instruction to complete this.programState.Text = "Inst " + (this.program_counter + 1) + " of " + size + ": " + inst.ToString(); break; } } } while (true) { Command c = comms.SimulatorGetCommand(); Reply r; switch (c.t) { case CommandType.CommandNone: return; case CommandType.CommandConnect: r = new Reply(); r.red = r.green = r.blue = 0x20; r.t = ReplyType.ReplyConnected; comms.SimulatorSendReply(r); for (int i = 0; i < Comms.num_programs; i++) { r = new Reply(); r.program_number = i; r.program_bytes = readProgram(i); r.t = ReplyType.ReplyProgram; comms.SimulatorSendReply(r); } r = new Reply(); r.errorCode = "Simulated mode"; r.t = ReplyType.ReplyMsg; comms.SimulatorSendReply(r); this.display.Text = "C"; this.Show(); break; case CommandType.CommandSetColour: this.simulated_program = null; this.setColour(c.red, c.green, c.blue); this.r = c.red; this.g = c.green; this.b = c.blue; this.programState.Text = ""; break; case CommandType.CommandGetColour: this.simulated_program = null; r = new Reply(); r.red = this.r; r.green = this.g; r.blue = this.b; r.t = ReplyType.ReplyGotColour; comms.SimulatorSendReply(r); break; case CommandType.CommandSetDisplay: this.simulated_program = null; setDisplay(c.value); this.programState.Text = ""; break; case CommandType.CommandExit: this.simulated_program = null; break; case CommandType.CommandRunEEPROMProgram: this.simulated_program = new InstructionList(readProgram(c.value)); startProgram(comms, "" + c.value); break; case CommandType.CommandRunTemporaryProgram: this.simulated_program = new InstructionList(c.program_bytes); startProgram(comms, "(temporary)"); break; case CommandType.CommandSaveEEPROMProgram: writeProgram(c.program_number, c.program_bytes); r = new Reply(); r.t = ReplyType.ReplyProgram; r.program_number = c.program_number; r.program_bytes = readProgram(c.program_number); comms.SimulatorSendReply(r); r = new Reply(); r.errorCode = "Written to simulated EEPROM"; r.t = ReplyType.ReplyMsg; comms.SimulatorSendReply(r); break; } } }
private void startProgram(SimulatorComms comms, string name) { Reply r; this.start_time = DateTime.Now.Ticks; this.program_time = 0; this.program_counter = 0; if (this.simulated_program.contents.Count() == 0) { r = new Reply(); r.errorCode = "Program " + name + " contains no instructions."; r.t = ReplyType.ReplyMsg; comms.SimulatorSendReply(r); this.simulated_program = null; } else if (this.simulated_program.end_time <= 0) { r = new Reply(); r.errorCode = "Program " + name + " has no running time."; r.t = ReplyType.ReplyMsg; comms.SimulatorSendReply(r); this.simulated_program = null; } else { r = new Reply(); r.errorCode = "Program " + name + " simulating..."; r.t = ReplyType.ReplyMsg; comms.SimulatorSendReply(r); } this.programState.Text = r.errorCode; }