399 lines
16 KiB
JavaScript
399 lines
16 KiB
JavaScript
class ClockElement extends Element {
|
|
ClockTick() {
|
|
if (this.Inputs[0]) {
|
|
this.Output = ~this.Output;
|
|
if (this.Output) {
|
|
this.Task.Time = Math.round(this.Period * this.Duty);
|
|
} else {
|
|
this.Task.Time = this.Period - Math.round(this.Period * this.Duty);
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
|
|
for (let a = 0; a < this.OutputConnections.length; a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
|
}
|
|
}
|
|
}
|
|
|
|
Delete() {
|
|
super.Delete();
|
|
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
return this.Output;
|
|
}
|
|
|
|
setInput(Input, Value) {
|
|
|
|
super.setInput(Input, Value);
|
|
|
|
if (!this.Inputs[0]) {
|
|
this.Output = false;
|
|
this.Task.LastCall = 0;
|
|
this.Task.Enabled = false;
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
for (let a = 0; a < this.OutputConnections.length;a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
|
}
|
|
} else {
|
|
this.Task.Enabled = true;
|
|
}
|
|
}
|
|
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1);
|
|
this.removeProperty("Inputs");
|
|
this.Name = "Clock";
|
|
this.Period = 1000;
|
|
this.Duty = 0.5;
|
|
this.Output = false;
|
|
this.Width = 100;
|
|
this.Task = new Task("ClockTask","CLOCK",0,Math.round(this.Period * this.Duty),this.ClockTick.bind(this));
|
|
this.setInput(0,true);
|
|
this.removeProperty("Inputs");
|
|
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},1000,false,false,4,999999);
|
|
let dutyProperty = new ElementProperty("Duty","int",{CBObject: this,CBFunction: "setDuty"},50,false,false,0,100);
|
|
if (RestoreData) {
|
|
if (this.Height != RestoreData.Height) {
|
|
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
|
this.Height = RestoreData.Height;
|
|
}
|
|
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
|
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
|
periodProperty.Values = RestoreData.Properties[0].Values;
|
|
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
|
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
|
dutyProperty.DefaultValue = RestoreData.Properties[1].DefaultValue;
|
|
dutyProperty.CurrentValue = RestoreData.Properties[1].CurrentValue;
|
|
dutyProperty.Values = RestoreData.Properties[1].Values;
|
|
dutyProperty.Minimium = RestoreData.Properties[1].Minimium;
|
|
dutyProperty.Maximium = RestoreData.Properties[1].Maximium;
|
|
|
|
this.Period = periodProperty.CurrentValue;
|
|
this.Duty = dutyProperty.CurrentValue/100;
|
|
this.Task.Enabled = RestoreData.Task.Enabled;
|
|
this.Task.Time = RestoreData.Task.Time;
|
|
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
|
this.Task.CallCount = RestoreData.Task.CallCount;
|
|
}
|
|
this.Properties.push(periodProperty);
|
|
this.Properties.push(dutyProperty);
|
|
this.LogicEngine.Scheduler.addTask(this.Task);
|
|
}
|
|
|
|
toJSON(key) {
|
|
let superjson = super.toJSON(key);
|
|
superjson.Task = {
|
|
Enabled: this.Task.Enabled,
|
|
Time: this.Task.Time,
|
|
LastCall: Date.now() - this.Task.LastCall,
|
|
CallCount: this.Task.CallCount
|
|
};
|
|
return superjson;
|
|
}
|
|
|
|
setPeriod(period) {
|
|
this.Period = period;
|
|
this.Task.LastCall = 0;
|
|
this.getProperty("Period").CurrentValue = period;
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
setDuty(duty) {
|
|
this.Duty = duty/100;
|
|
this.Task.LastCall = 0;
|
|
this.getProperty("Duty").CurrentValue = duty;
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
drawElement(x, y, ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
|
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms " + (this.Duty * 100) + "%","10px Console");
|
|
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_CLOCK = new ElementCatalog_Element("Clock","The clock will output a pulse for the length of the duty cycle for a given period. I.E. if a 1000ms period with 50% duty cycle is given, the output will be high for 500ms and low for 500ms. The clock can be disabled with a low input, this also resets the clocks start time to allow for syncing.","🕑",ClockElement,[]);
|
|
ElementReferenceTable.push(ElementCatalog_CLOCK);
|
|
ElementCategory_Timing.addElement(ElementCatalog_CLOCK);
|
|
|
|
class PulseElement extends Element {
|
|
ClockTick() {
|
|
this.Output = false;
|
|
this.Task.Enabled = false;
|
|
this.Task.LastCall = Date.now();
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
|
|
for (let a = 0; a < this.OutputConnections.length; a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
|
}
|
|
}
|
|
|
|
Delete() {
|
|
super.Delete();
|
|
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
return this.Output;
|
|
}
|
|
|
|
setInput(Input, Value) {
|
|
if (Input > 0) return;
|
|
|
|
Value = this._Container.isHigh(this,Input);
|
|
//super.setInput(Input, Value);
|
|
this.Inputs[Input] = (Value === false) ? false : true;
|
|
|
|
if (this.Inputs[0] && !this.Task.Enabled) {
|
|
this.Output = true;
|
|
for (let a = 0; a < this.OutputConnections.length;a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
|
}
|
|
this.Task.LastCall = Date.now();
|
|
this.Task.Enabled = true;
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1);
|
|
this.removeProperty("Inputs");
|
|
this.Name = "Pulse";
|
|
this.Period = 100;
|
|
this.Output = false;
|
|
this.Width = 100;
|
|
this.Task = new Task("PulseTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
|
|
this.removeProperty("Inputs");
|
|
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
|
|
if (RestoreData) {
|
|
if (this.Height != RestoreData.Height) {
|
|
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
|
this.Height = RestoreData.Height;
|
|
}
|
|
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
|
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
|
periodProperty.Values = RestoreData.Properties[0].Values;
|
|
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
|
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
|
|
|
this.Period = periodProperty.CurrentValue;
|
|
|
|
this.Task.Enabled = RestoreData.Task.Enabled;
|
|
this.Task.Time = RestoreData.Task.Time;
|
|
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
|
this.Task.CallCount = RestoreData.Task.CallCount;
|
|
}
|
|
|
|
|
|
this.Properties.push(periodProperty);
|
|
this.LogicEngine.Scheduler.addTask(this.Task);
|
|
}
|
|
|
|
toJSON(key) {
|
|
let superjson = super.toJSON(key);
|
|
superjson.Task = {
|
|
Enabled: this.Task.Enabled,
|
|
Time: this.Task.Time,
|
|
LastCall: Date.now() - this.Task.LastCall,
|
|
CallCount: this.Task.CallCount
|
|
};
|
|
return superjson;
|
|
}
|
|
|
|
|
|
setPeriod(period) {
|
|
this.Period = parseInt(period);
|
|
this.Task.Time = parseInt(period);
|
|
this.getProperty("Period").CurrentValue = parseInt(period);
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
drawElement(x, y, ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
|
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
|
|
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms","10px Console");
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_PULSE = new ElementCatalog_Element("Pulse","The pulse upon input will output a high only for as long as the period set, I.E. if a period of 1000ms is set then any input no matter how long, will trigger a pulse 1000ms long.","|\\__",PulseElement,[]);
|
|
ElementReferenceTable.push(ElementCatalog_PULSE);
|
|
ElementCategory_Timing.addElement(ElementCatalog_PULSE);
|
|
|
|
class DelayElement extends Element {
|
|
ClockTick() {
|
|
if (this.Output) {
|
|
this.Output = false;
|
|
this.Task.Enabled = false;
|
|
this.Task.LastCall = Date.now();
|
|
this.Task.Time = this.Period;
|
|
if (this.InputPulses.length > 0) {
|
|
this.Task.Enabled = true;
|
|
}
|
|
} else {
|
|
this.Output = true;
|
|
this.Task.Enabled = false;
|
|
if (this.InputPulses.length > 0) {
|
|
this.Task.Time = this.InputPulses[0];
|
|
this.Task.Enabled = true;
|
|
this.InputPulses.splice(0,1);
|
|
} else {
|
|
if (this.InputEnd > 0) {
|
|
this.Task.Time = this.InputEnd - this.InputStart;
|
|
this.Task.Enabled = true;
|
|
}
|
|
}
|
|
this.Task.LastCall = Date.now();
|
|
}
|
|
|
|
for (let a = 0; a < this.OutputConnections.length; a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input, this.getOutput());
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
Delete() {
|
|
super.Delete();
|
|
this.LogicEngine.Scheduler.deleteTask(this.Task);
|
|
}
|
|
|
|
getOutput(Output=0) {
|
|
if (super.getOutput() === 0) return false;
|
|
return this.Output;
|
|
}
|
|
|
|
setInput(Input, Value) {
|
|
if (Input > 0) return;
|
|
Value = this._Container.isHigh(this,Input);
|
|
if (this.Inputs[Input] == (Value === false) ? false : true) return;
|
|
//super.setInput(Input, Value);
|
|
this.Inputs[Input] = Value;
|
|
|
|
if (this.Inputs[0] && !this.Task.Enabled) {
|
|
this.InputStart = Date.now();
|
|
this.InputEnd = 0;
|
|
this.Output = false;
|
|
for (let a = 0; a < this.OutputConnections.length;a++) {
|
|
this.LogicEngine.RecursionCount = 0;
|
|
this.OutputConnections[a].Element.setInput(this.OutputConnections[a].Input,this.getOutput());
|
|
}
|
|
this.Task.LastCall = Date.now();
|
|
this.Task.Enabled = true;
|
|
} else {
|
|
if (!this.Inputs[Input]) {
|
|
this.InputEnd = Date.now();
|
|
if (this.InputPulses.length > 0) {
|
|
this.InputPulses.push(this.InputEnd - this.InputStart);
|
|
this.InputStart = 0;
|
|
this.InputEnd = 0;
|
|
} else {
|
|
if (!this.Task.Enabled) {
|
|
this.Task.LastCall = Date.now();
|
|
this.Task.Time = (this.InputEnd - this.InputStart) - (Date.now() - this.InputEnd);
|
|
this.Task.Enabled = true;
|
|
} else {
|
|
this.InputPulses.push(this.InputEnd - this.InputStart);
|
|
}
|
|
}
|
|
} else {
|
|
// We are in a condition where input went true, but the task is already enabled
|
|
this.InputStart = Date.now();
|
|
}
|
|
}
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
constructor(_Container, RestoreData = null, logicengine) {
|
|
super(_Container, RestoreData,logicengine,1);
|
|
this.removeProperty("Inputs");
|
|
this.Name = "Delay";
|
|
this.Period = 100;
|
|
this.Output = false;
|
|
this.Width = 100;
|
|
this.Task = new Task("DelayTask","CLOCK",0,this.Period,this.ClockTick.bind(this));
|
|
this.Task.Enabled = false;
|
|
this.removeProperty("Inputs");
|
|
let periodProperty = new ElementProperty("Period","int",{CBObject: this,CBFunction: "setPeriod"},100,false,false,4,999999);
|
|
if (RestoreData) {
|
|
if (this.Height != RestoreData.Height) {
|
|
// Oddly this height value gets set to 25000 on return from the above super(), if anyone has any ideas why let me know!
|
|
this.Height = RestoreData.Height;
|
|
}
|
|
periodProperty.DefaultValue = RestoreData.Properties[0].DefaultValue;
|
|
periodProperty.CurrentValue = RestoreData.Properties[0].CurrentValue;
|
|
periodProperty.Values = RestoreData.Properties[0].Values;
|
|
periodProperty.Minimium = RestoreData.Properties[0].Minimium;
|
|
periodProperty.Maximium = RestoreData.Properties[0].Maximium;
|
|
|
|
this.Period = periodProperty.CurrentValue;
|
|
|
|
this.Task.Enabled = RestoreData.Task.Enabled;
|
|
this.Task.Time = RestoreData.Task.Time;
|
|
this.Task.LastCall = RestoreData.Task.LastCall + Date.now();
|
|
this.Task.CallCount = RestoreData.Task.CallCount;
|
|
}
|
|
this.Properties.push(periodProperty);
|
|
this.InputStart = 0;
|
|
this.InputEnd = 0;
|
|
this.InputPulses = new Array();
|
|
this.Inputs[0] = false;
|
|
this.LogicEngine.Scheduler.addTask(this.Task);
|
|
}
|
|
toJSON(key) {
|
|
let superjson = super.toJSON(key);
|
|
superjson.InputStart = this.InputStart;
|
|
superjson.InputEnd = this.InputEnd;
|
|
superjson.InputPulses = this.InputPulses;
|
|
superjson.Task = {
|
|
Enabled: this.Task.Enabled,
|
|
Time: this.Task.Time,
|
|
LastCall: Date.now() - this.Task.LastCall,
|
|
CallCount: this.Task.CallCount
|
|
};
|
|
return superjson;
|
|
}
|
|
|
|
setPeriod(period) {
|
|
this.Period = parseInt(period);
|
|
this.Task.Time = parseInt(period);
|
|
this.getProperty("Period").CurrentValue = parseInt(period);
|
|
this.drawElement(0,0,this.StaticCtx);
|
|
}
|
|
|
|
drawElement(x, y, ctx) {
|
|
if (this.LogicEngine.ActiveContainer !== this._Container) return; // No point if we aren't visible
|
|
this.StaticCanvas.width = this.Width;
|
|
this.StaticCanvas.height = this.Height;
|
|
|
|
this.drawBorderBox(ctx, x+10,y,this.Width-20,this.Height,1,"#000","#f7e979",this.LogicEngine.Settings.ShadowColor);
|
|
this.drawTextCentered(ctx,x,y+5,this.Width,12,this.Period + "ms","10px Console");
|
|
if (this?.Task?.Enabled) this.drawTextCentered(ctx,x,y+this.Height-16,this.Width,12,(this.Task.Time - (Date.now() - this.Task.LastCall)) + "ms " + "(" + this.InputPulses.length + ")","10px Console");
|
|
this.drawTextCentered(ctx,x,y,this.Width,this.Height,this.Designator,"12px Console","#000");
|
|
this.drawInputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
this.drawOutputs(ctx,x,y,undefined,undefined,undefined,undefined,true);
|
|
}
|
|
}
|
|
let ElementCatalog_DELAY = new ElementCatalog_Element("Delay","The delay element will delay any input signal for the given period then output it for the same length of time it came in. Inputs will be buffered and played back out at the period rate.","__|\\",DelayElement,[]);
|
|
ElementReferenceTable.push(ElementCatalog_DELAY);
|
|
ElementCategory_Timing.addElement(ElementCatalog_DELAY);
|