Click here to Skip to main content
15,880,405 members
Articles / Web Development / HTML

Circuit Engine

Rate me:
Please Sign up or sign in to vote.
4.93/5 (103 votes)
18 Oct 2018GPL355 min read 247.8K   8.6K   212  
A System for Simulation and Analysis of Logic Circuits
/*
This file is part of Circuit Engine.
 
Circuit Engine 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.
 
Circuit Engine 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 Circuit Engine.  If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
	CEngine.h : The commander of other classes. He manages the CEs and
				BreadBoard, and handles communication and organization
				between them with the help of Artist.h.
*/
#pragma once
#include "BreadBoard.h"
#include "CircuitElement.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <io.h>

//CEC file buffer definitions

typedef struct CircuitElementHeadBuf
{
	CEtype type;
	unsigned char CEInfo;
	char name [27];
	unsigned short V_leg_no;
	unsigned short G_leg_no;
	unsigned int Position_X1;
	unsigned int Position_Y1;
	unsigned int nof_inputs;
	unsigned int nof_outputs;
	unsigned int nof_gates;
} CEH_BUF;

typedef struct GateHeadBuf
{
	GateType type;
	unsigned short nof_inputs;
	unsigned short leg_no;
} GH_BUF;

typedef struct BreadBoardBuf
{
	unsigned int rows;
	unsigned int cols;
	unsigned int nof_holes_in_a_node;
} BB_BUF;

//struct camcone is also used for the last cam position

//eof CEC file definitions


enum TypeInsideChipBlock {ticbVoltage,ticbGround,ticbInput,ticbGate,ticbEND,ticbEndchip,ticbEmpty};

class CECircuit //Circuit Engine Circuit.
{
	friend void CalculateTraverseCircuit(void);
	friend void AddCable(void);
	friend void AddLED(void);
	friend void AddSourceBar(bool SourceBit);
	friend void SpecialKeys(int key, int x, int y);
	friend void InitArtist();
	friend void initOGL(void);
	friend void DrawCircuit(void);
	friend void Keyboard(unsigned char key, int x, int y);
protected:
	CircuitElement *circuit; // the circuit
	BreadBoard *theBreadBoard; // the bread-board
	unsigned int nof_elements; //nof CEs added into the circuit.

private:

	//Parse Check Variables used by ParseScriptFile() (CEngine Parser).
	CircuitElement *CurrentCE; // temporary place for compilation procedure.
	Gate *CurrentGate; // is the last added into a circuit element.(for connection purpose)
	FILE *ScriptFile;
	//SAVE FILE WILL BE HERE ALSO.

	//gate connection variables. using by AddGate() and ParseScriptFile().
	static unsigned int creation_counter; //is to find the <creation_ordered>th gate in the CurrentCE.
	static unsigned int connection_counter; //is for Index increment of Input array of the current Gate which is adding into the CurrentCE.

	void GiveError(char *e, unsigned int line = 0,char *add = NULL);
	void GiveMsg(char *msg);
	void GiveCompMsg(char *msg,char *a1 = NULL,char *a2 = NULL, char *a3 = NULL, char *a4 = NULL);
	GateType ChrToGateType(char * GateName); // for script parsing.

public:
	CECircuit();
	~CECircuit();

	//Script file operations.
	void ParseScriptFile(char *file_path);//Compiles CESL file
	void SaveCircuit(void); //saves current circuit as CEC file
	void LoadCircuit(char *file_path); //Loads CEC file

	//These 4 methods is for Circuit Element Insertion Block.
	void NewCurrentCE(void);
	void AddGate(GateType new_type,unsigned short new_nof_inputs, unsigned short new_leg_no);
	void Connect(GateType gate_type, unsigned creation_order);
	void AddCurrentCE(void); //Add Current circuit element into the circuit.
	void DeleteCurrentCE(void);
	void RemoveCE(CircuitElement *CE);
	void SetLegPlugAbilityOfCurrentCE (void);

	void PrintCurrentCircuit(void);
	void InsertTempVG(void);

	void debugPrintCable(void)
	{
		CircuitElement *cable = circuit;

		for(;cable->GetType() != cetCABLE; cable = cable->GetNext());

		for(;cable != circuit->GetNext();cable = cable->GetNext())
		{
			printf("\n\nCABLE %p (%d,%d)",cable,cable->GetPositionX(),cable->GetPositionY());
            printf("\nCable Node ID = %d",cable->V.leg_no);
		}

	}

};

unsigned int CECircuit::creation_counter = 0;
unsigned int CECircuit::connection_counter = 0;

void CECircuit::GiveError(char *e, unsigned int line, char *add)
{
	char *space = "";
	if(!line)
		printf("\n(e)(CEngine): %s.",e);
	else
	{
		if(add == NULL) add = space;
		printf("\n(e)(CEngine) Syntax Error on Line(%d) : %s%s.",line,e,add);
	}

	this->~CECircuit();
	printf("\n(!)(CEngine) : CEngine has been halted.");

	exit(0);
}

void CECircuit::GiveMsg(char *msg)
{
	printf("\n(i)(CEngine): %s.",msg);
}

void CECircuit::GiveCompMsg(char *msg,char *a1,char *a2, char *a3, char *a4)
{
	char buffer[200];
	sprintf(buffer,msg,a1,a2,a3,a4);
	printf("\n(c)(CEngine): %s.",buffer);
}

void CECircuit::PrintCurrentCircuit(void)
{
	unsigned int nof_e = nof_elements;
	CircuitElement *pick_ce = circuit;
	printf("\n\nLISTING CURRENT CIRCUIT ELEMENTS:\n");
	printf("\nBREADBOARD : %d rows, %d cols and %d holes in a node.\n", CurrentBBrow,CurrentBBcol,CurrentBBstride);
	do
	{
		pick_ce->print();
		pick_ce = pick_ce->GetNext();
		printf("\n");
		nof_e--;
	}
	while(nof_e);

	printf("\nEND OF CIRCUIT LIST\n\n",nof_elements);
}

CECircuit::CECircuit()
{
	theBreadBoard = NULL;
	CurrentCE = NULL;
	circuit = NULL;
	nof_elements = 0;
	ScriptFile = NULL;
	CurrentGate = NULL;

	//Create Source CE (just V&G).
	GiveMsg("Creating source level (V&G)...");
	NewCurrentCE();
	CurrentCE->SetName("Source(V&G)");
	AddGate(gtVoltage,0,0);
	AddGate(gtGround,0,0);
	AddCurrentCE();
}

CECircuit::~CECircuit()
{
	CircuitElement *pick_ce = circuit;
	CircuitElement *prev_element = NULL;

	for(unsigned int i=0; i<nof_elements; i++)
	{
		prev_element = pick_ce;
		pick_ce = pick_ce->GetNext();
		delete prev_element;
	}
	circuit = NULL;

	if(CurrentCE != NULL) DeleteCurrentCE();
	if(theBreadBoard!=NULL) theBreadBoard->~BreadBoard();
}

void CECircuit::ParseScriptFile(char *file_path)
{
	char buf[512];

	Binary LegAssociations; // 'set state' of each leg number is hold in index (leg_number - 1).i.e. starts from zero instead of one.
	unsigned short total_number_of_legs;
	bool Current_CHIP_has_NOTAVAILABLE_leg;
	unsigned int current_chip_nof_inputs, current_chip_nof_outputs;

	CEtype				TInsideScript;
	TypeInsideChipBlock	TInsideChipBlock;
	GateType			TInsideGateBlock;

	unsigned short depth = 0; // 0 in script,1 in chip,2 in gate blocks.

	unsigned long line_counter;
	char *token;
	char *delims = "	\n ";//delimiters are 'tab, new-line, space'

	unsigned int ce_leg_no;
	unsigned int BBrow,BBcol,BBstride;

	//variables for connection process in depth 1:ticbEndchip 
	Gate *pick_GATE;
	Gate *connect_gate;
	unsigned int i,j;


	if(file_path == NULL) GiveError("no given script file");
	if((ScriptFile = fopen(file_path,"r")) == NULL)
	{
		GiveCompMsg("Script file couldn't be found (%s)", file_path);
		GiveError("CEngine parser is halted");
	}

	GiveCompMsg("compilation started (%s)...",file_path);

	for(line_counter = 1; !feof(ScriptFile); line_counter++) //find and load the breadboard configuration.
	{
		fgets(buf,511,ScriptFile); //get line.
		token = strtok(buf,delims); // go on to the first token on the line.

		if(token == NULL || !strcmp(token,"//")) // an empty line or a comment.
			continue;

		//Get BreadBoard Information (row, col, stride)
		 if(strcmp(token,"BREADBOARD")) //first statement in the CESL file must be the BREADBOARD row col stride
             GiveError("First statement in the .CESL file must be BREADBOARD <row> <col> <nof_holes_in_one_node>",line_counter);

		 //resolve the BreadBoard dimensions
		 token = strtok(NULL,delims); //on <BBrow>.
		 if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line, there must be <BBrow> <BBcol> <nof_holes_in_one_node>",line_counter);
		 BBrow = atoi(token);
		 if(BBrow == 0) GiveError("Invalid <BBrow>",line_counter);

		 token = strtok(NULL,delims); //on <BBcol>.
		 if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line, there must be <BBcol> <nof_holes_in_one_node>",line_counter);
		 BBcol = atoi(token);
		 if(BBcol < 2) GiveError("Invalid <BBcol>, must be at least 2",line_counter);

		 token = strtok(NULL,delims); //on <BBstride>.
		 if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line, there must be <nof_holes_in_one_node>",line_counter);
		 BBstride = atoi(token);
		 if(BBstride < 2) GiveError("Invalid <nof_holes_in_a_node>, must be at least 2",line_counter);

		 theBreadBoard = new BreadBoard(BBrow,BBcol,BBstride);
		 CurrentBBrow = BBrow;
		 CurrentBBcol= BBcol;
		 CurrentBBstride = BBstride;
		 line_counter++;
		 break; //eof Get BreadBoard Information		
	}

	for(; !feof(ScriptFile); line_counter++)
	{	
		fgets(buf,511,ScriptFile); //get line.
		token = strtok(buf,delims); // go on to the first token on the line.

		if(token == NULL || !strcmp(token,"//")) // an empty line or a comment.
			continue;

		else //TOKEN_ON_BLOCK_HEAD
		{
			if(depth == 0) // IN_SCRIPT_ROOT
			{
				TInsideScript    = !strcmp(token,"CHIP") ? cetCHIP : !strcmp(token,"BREADBOARD") ? cetBREADBOARD : cetEmpty;

				if(TInsideScript != cetBREADBOARD)
				{
					NewCurrentCE();
					CurrentCE->SetType(TInsideScript);
				}

				switch(TInsideScript)
				{
				case cetCHIP://CHIP <ce_name> <ce_nof_inputs> <ce_nof_outputs>

					//Place into the initial position.
					CurrentCE->MoveTo(theBreadBoard->GetStrideValue()-1,0);//initially a Chip is on the first 'between cols'(on the first Hollow and will always be moved over hollows...)

					token = strtok(NULL,delims);//on name.
					if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line, there must be <ce_name> <ce_nof_inputs> <ce_nof_outputs>",line_counter);
					CurrentCE->SetName(token);

					token = strtok(NULL,delims); //on nof_inputs.
					if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line, there must be <ce_nof_inputs> and <ce_nof_outputs>",line_counter);
					current_chip_nof_inputs = atoi(token);
					if(current_chip_nof_inputs == 0) GiveError("invalid number of inputs", line_counter);
					CurrentCE->SetNofInputs(current_chip_nof_inputs);

					token = strtok(NULL,delims); //on nof_outputs.
					if(token == NULL || !strncmp(token,"//",1)) GiveError("Unexpected end of line. no <ce_nof_outputs>",line_counter);
					current_chip_nof_outputs = atoi(token);
					if(current_chip_nof_outputs == 0) GiveError("invalid number of outputs", line_counter);
					CurrentCE->SetNofOutputs(current_chip_nof_outputs);

					depth = 1;//down to depth 1 : IN_CHIP_BLOCK
					GiveCompMsg("CE %s:%s is now setting up by in-script information ...",CEnames[CurrentCE->GetType()],CurrentCE->GetName());

					//initialize binary leg association control array which is used during parsing.
					total_number_of_legs = current_chip_nof_inputs + current_chip_nof_outputs + 2; // here + 2 for V&G legs of the chip.
					total_number_of_legs += (Current_CHIP_has_NOTAVAILABLE_leg = (total_number_of_legs % 2) ? true : false) ? 1L : 0L; // make chip even number of legs.
					if( (unsigned int)(total_number_of_legs/2) > CurrentBBrow) GiveError("Chip size exceeds breadboard rows -> ",line_counter,CurrentCE->GetName());
					LegAssociations.CreateBitArray( total_number_of_legs + 2);

					//>LegAssociations array STARTS FROM ZERO. Set bit of a leg is (leg_number - 1).
					//>explanation for (+2) more bits in the array, These bits are special and have different meanings than other bits:
					//>index total_number_of_legs = Is voltage leg set? (index before last)
					//>index total_number_of_legs = Is ground leg set? (last index)

					break;

				case cetBREADBOARD:	GiveError("BREADBOARD statement is duplicated",line_counter); break;
				case cetEmpty:		GiveError("Unrecognized CESL keyword!",line_counter); break;
				default:			GiveError("Unexpected CEngine Parser failure!",line_counter); break;
				}
			}//EOF IN_SCRIPT

			else if(depth == 1) //DEPTH 1: IN_CHIP_BLOCK.
			{//inside chip block. //token is on an inside-chip block.

				TInsideChipBlock = !strcmp(token,"VOLTAGE") ? ticbVoltage : !strcmp(token,"GROUND") ? ticbGround : !strcmp(token,"INPUT") ? ticbInput : !strcmp(token,"GATE") ? ticbGate : !strcmp(token,"END") ? ticbEndchip : ticbEmpty;

				if(CurrentCE == NULL) GiveError("Element without CHIP block!.",line_counter);

				switch(TInsideChipBlock)
				{
				case ticbVoltage:

					token = strtok(NULL,delims); // token is on <ce_leg_no>(Voltage connection)
					if(token == NULL || !strncmp(token,"/",1)) GiveError("Unexpected end of line. Voltage must have <ce_leg_no>",line_counter);
					ce_leg_no = atoi(token);
					if(ce_leg_no == 0) GiveError("Voltage must have <ce_leg_no> (other than 0)",line_counter);

					if( ce_leg_no > total_number_of_legs ) GiveError("Specified legs are greater than the total number of legs declared in the CHIP header",line_counter);
					if( LegAssociations[ce_leg_no-1] ) GiveError("The leg number is assigned more than once",line_counter);
					LegAssociations.SetIndex(ce_leg_no - 1); // indicate that this leg is occupied.

					LegAssociations.SetIndex( total_number_of_legs ); //indicate that V leg is set.
					CurrentCE->SetVoltageLegNo(ce_leg_no);

					break;

				case ticbGround:

					token = strtok(NULL,delims); // token is on <ce_leg_no>(Ground connection)
					if(token == NULL || !strncmp(token,"/",1)) GiveError("Unexpected end of line. Ground must have <ce_leg_no>",line_counter);
					ce_leg_no = atoi(token);
					if(ce_leg_no == 0) GiveError("Ground must have <ce_leg_no> (other than 0)",line_counter);

                    if( ce_leg_no > total_number_of_legs ) GiveError("Specified legs are greater than the total number of legs declared in the CHIP header",line_counter);
					if( LegAssociations[ce_leg_no-1] ) GiveError("The leg number is assigned more than once",line_counter);
					LegAssociations.SetIndex(ce_leg_no - 1); // indicate that this leg is occupied.

					LegAssociations.SetIndex( total_number_of_legs + 1 ); //indicate that G leg is set.
					CurrentCE->SetGroundLegNo(ce_leg_no);

					break;

				case ticbInput:

					token = strtok(NULL,delims); // token is on <ce_leg_no>(Input connection)
					if(token == NULL || !strncmp(token,"/",1)) GiveError("INPUT must be associated a <ce_leg_no>",line_counter);					
					ce_leg_no = atoi(token);
					if(ce_leg_no == 0) GiveError("INPUT must have <ce_leg_no> (other than 0)",line_counter);

					if( ce_leg_no > total_number_of_legs ) GiveError("Specified legs are greater than the total number of legs declared in the CHIP header",line_counter);
					if( LegAssociations[ce_leg_no-1] ) GiveError("The leg number is assigned more than once",line_counter);
					LegAssociations.SetIndex( ce_leg_no-1 ); // indicate that this leg is occupied.

					CurrentCE->AddGate(gtInput,1,ce_leg_no);
                    if(current_chip_nof_inputs-- == 0) GiveError("Specified number of inputs does not match the declared in CHIP block header",line_counter);

					break;

				case ticbGate://GATE <gtType> <gt_nof_inputs> [output <ce_leg_no>]

					GateType TCurrentGate;
					unsigned short nof_inputs;

					token = strtok(NULL,delims);// <gtType> .
					if(token == NULL || !strncmp(token,"/",1)) GiveError("Unexpected end of line. > GATE <gt_type> <nof_inputs> [output <ce_leg_no>]",line_counter);
					TCurrentGate = Gate::ChrToGateType(token); // get gtType.

					token = strtok(NULL,delims); // <gt_nof_inputs>
					if(token == NULL || !strncmp(token,"/",1)) GiveError("Unexpected end of line. > GATE <gt_type> <gt_nof_inputs> [output <ce_leg_no>]",line_counter);
					nof_inputs = atoi(token);
					if(nof_inputs == 0) GiveError("Invalid number of inputs for gate",line_counter,GateNames[TCurrentGate]);

					ce_leg_no = 0; //if no [output] part, zero will be the the output leg number (i.e. no outputting gate).
					token = strtok(NULL,delims); // [output <ce_leg_no>] or CONNECTION.
					if(token != NULL && !strcmp(token,"output")) // if optional output leg is specified.
					{
						token = strtok(NULL,delims); // token is on <ce_leg_no> (output leg number).
						if(token == NULL || !strncmp(token,"/",1)) GiveError("Unexpected end of line",line_counter);
						ce_leg_no = atoi(token);
						if(ce_leg_no == 0) GiveError("Invalid output leg declaration for gate",line_counter,GateNames[TCurrentGate]);

						if( ce_leg_no > total_number_of_legs ) GiveError("Gate leg number exceeds the total number of legs declared in the CHIP header",line_counter);
						if( LegAssociations[ce_leg_no-1] ) GiveError("The leg number is assigned more than once",line_counter);
						LegAssociations.SetIndex( ce_leg_no-1 );
						if(current_chip_nof_outputs-- == 0) GiveError("Specified number of outputs does not match the declared in CHIP block header",line_counter);
					}

					//type , nof_inputs and output-legs are ready. Now Add the gate to CurrentCE.
					AddGate(TCurrentGate, nof_inputs, ce_leg_no);
					
					//this is the end of a GATE declaration (Gate Header), after that a new line
					//comes out. The connection part requires an new switch statement
					//with the use of TypeInsideGateBlock type.
					//thus, another else if structure is required for general parsing structure (depth 2)...

					depth = 2; // down to IN_GATE_BLOCK
												
					break;

				case ticbEndchip:

					//check if the "END" keyword concludes with "CHIP" keyword.if not, give block error.
					token = strtok(NULL,delims);
					if(token == NULL || !strncmp(token,"//",2)) GiveError("Unexpected end of line. Expecting keyword CHIP",line_counter);
					if(strcmp(token,"CHIP")) GiveError("Unrecognized 'END <ce_block>' block header",line_counter);
					if( !LegAssociations[ total_number_of_legs ] ) GiveError("The CHIP has not a VOLTAGE leg declaration",line_counter);
					if( !LegAssociations[ total_number_of_legs + 1 ] ) GiveError("The CHIP has not a GROUND leg declaration",line_counter);
					if( LegAssociations.GiveNofOnes() != (total_number_of_legs - Current_CHIP_has_NOTAVAILABLE_leg  + 2) )
					{	//number of ones in LegAssociations array must be equal to
						//tot_nof_legs - N/A(if there is one -- i.e. if user must omit one leg as N/A) + 2(special check bits of V&G, las two bits of the array)
						GiveError("defined nof inputs and outputs in header does't macth with nof leg associations, check nof_inputs, nof_outputs, voltage and ground leg specifications");
					}

					CurrentCE->LegPlugAbility.CreateBitArray(CurrentCE->GetNofLegs());

					_itoa(CurrentCE->GetID(),buf,10);
					GiveCompMsg("Gate List is OK for %s.%s.",CurrentCE->GetName(),buf);
					GiveCompMsg("Gate connection process is starting for %s.%s...",CurrentCE->GetName(),buf);

					//As explained in the IN_GATE_BLOCK, here is the converting process
					//of creation_no's into in-memory addresses before the CurrentCE
					//is added into the circuit:

                    //CONVERTING PROCESS (creation_no to in-memory-gate-address)					
					for(pick_GATE = CurrentCE->GetHead() ; pick_GATE!=NULL ; pick_GATE = pick_GATE->GetNext())
					{
                        if(pick_GATE->GetGateType() == gtInput) continue; //ignore input gate connection (it'll be done in CEngine)
						for(i=0; i<pick_GATE->GetNofInputs(); i++)
						{//connect corresponding input array element which it points to by its creation_no inside.
                            //proceed onto the specified gate by the creation_no.
							for(j=0,connect_gate = CurrentCE->GetHead(); j<(unsigned int)pick_GATE->Input[i] ; ++j,connect_gate = connect_gate->GetNext());
							//now the connect_gate is on the gate which is to be connected to pick_GATE->Input[i].
							pick_GATE->Input[i] = connect_gate;
						}

					}
					//eof CONVERTING PROCESS.

					GiveCompMsg("Gate connection process is completed.");

					AddCurrentCE(); // Chip declaration is completed. Add Current Circuit Element to the Circuit.					

					depth = 0; // up to IN_SCRIPT_ROOT.

					break;

				case ticbEmpty:	GiveError("Unrecognized CESL keyword : expected 'END CHIP'",line_counter);break;
				default:		GiveError("Unexpected CEngine failure!",line_counter);break;
				}
			}//EOF IN_CHIP_BLOCK

			else if(depth == 2) // DEPTH 2: IN_GATE_BLOCK.
			{

				TInsideGateBlock = !strcmp(token,"INPUT") ? gtInput :!strcmp(token,"AND") ? gtAND : !strcmp(token,"NAND") ? gtNAND : !strcmp(token,"OR") ? gtOR : !strcmp(token,"NOR") ? gtNOR : !strcmp(token,"XOR") ? gtXOR : !strcmp(token,"XNOR") ? gtXNOR : !strcmp(token,"NOT") ? gtNOT : !strcmp(token,"END") ? gtGround : gtEmpty;
			    //here gtGround is used to recognize 'END GATE' block.

				unsigned int creation_no;
				Gate *pick_gate = CurrentCE->GetHead();

				//current gate is the last inserted gate into the CE.
				switch(TInsideGateBlock)
				{
					case gtAND:
					case gtNAND:
					case gtOR:
					case gtNOR:
					case gtXOR:
					case gtXNOR:
					case gtNOT:
					case gtInput:

						if(connection_counter == CurrentGate->GetNofInputs())
							GiveError("number of inputs is too much",line_counter);

						token = strtok(NULL,delims);//token must be on <creation_no>
						if(token == NULL || !strncmp(token,"/",1)) GiveError("Expecting <creation_order>",line_counter);
						creation_no = atoi(token); //<creation_no of the gate to be connected.>
						
						//Connect(TInsideGateBlock,creation_no);

						//->instead of real address connection, put into input array the numerical connections
						//this will allow you to use the ability of sync/async circuit design of the data structure.
						//(a gate which is wanted to be connected to current gate may not be created(declared) currently yet...)
						//(later this numerical connections can be converted to real addresses by retraversing the CurrentCE.)

                        //previously the connection was made by (gtType, creation_no) pair
						//but now, it is made by (creation_no) only!!!
						//(in order to make later conversion of these creation_no's into real addresses)
						//UPDATE THIS ALSO IN THE CESL DOCUMENTATION!.

                        CurrentGate->Input[connection_counter++] = (Gate*)creation_no;
						//this will converted into actual address of gate which will be connected to this gate.
						//at the end of the CHIP block.

						break;

					case gtGround://!!!here gtGround is used to recognize End gate block.

						if(connection_counter < CurrentGate->GetNofInputs())
							GiveError("number of inputs are less than declared",line_counter);

						token = strtok(NULL,delims);
						if(token == NULL) GiveError("Unexpected end of line",line_counter);
						//check if END is concluded by GATE or not.
						if(strcmp(token,"GATE")) GiveError("Cannot found corresponding GATE block header");
						depth = 1; // up to IN_CHIP_BLOCK depth.
						break;

					case gtEmpty:	GiveError("Unrecognized CESL keyword : expected 'END GATE'",line_counter);break;
					default:		GiveError("Unexpected CEngine failure!",line_counter);break;
				}

			}//EOF IN_GATE_BLOCK

			else //UNKNOWN BLOCK DEPTH
				GiveError("Unknown script depth!");
		}//EOF TOKEN_ON_BLOCK_HEAD

	}//CHECK NEXT LINE.

	fclose(ScriptFile);

	GiveMsg("compilation completed...");

	this->PrintCurrentCircuit();
}

void CECircuit::NewCurrentCE(void)
{
	if(CurrentCE != NULL) GiveError("Invalid Circuit Element Block!. May be you forgot to AddCurrentCE()");

	CurrentCE = new CircuitElement();
}

void CECircuit::AddGate(GateType new_type,unsigned short new_nof_inputs, unsigned short new_leg_no)
{
	if(CurrentCE == NULL) GiveError("CurrentCE is not ready for gate insertion");
	//if(CurrentGate != NULL) GiveMsg("Invalid AddGate() block"); This is handled by the CEngine Parser (using connections less or greater than declared Input Array size of the current Gate)
	CurrentGate = CurrentCE->AddGate(new_type,new_nof_inputs,new_leg_no);
	connection_counter = 0; //connection_counter is for Input array index of the Gate class
}

void CECircuit::Connect(GateType gate_type, unsigned int creation_order)
{
	if(CurrentCE == NULL) GiveError("CurrentCE is not ready for connection setting");

	unsigned int i;
	Gate *pick_gate = CurrentCE->GetHead();

	creation_counter = 0;

	if(pick_gate == NULL)
		GiveError("Current Circuit Element is not ready");

	for(i=0; i<CurrentCE->GetNofGates(); i++) //including the last current gate.
	{
		if(pick_gate->GetGateType() == gate_type)
		{
			if(creation_order == creation_counter)
			{
				CurrentGate->Connect(connection_counter, pick_gate); //meanwhile point to next input.
				printf("\n(c)(CEngine): connecting gate [%s(I%d)] -> [%s_%d]",GateNames[CurrentGate->GetGateType()], connection_counter , GateNames[gate_type], creation_order);
				++connection_counter;
				break;
			}
			creation_counter ++;
		}

		pick_gate = pick_gate->GetNext();
	}

	if(i == CurrentCE->GetNofGates())
	{
		//printf("%p",pick_gate);
		GiveError("Connection couldn't be made. Wrong gate declaration.");
	}
}

void CECircuit::AddCurrentCE(void)
{
	if(CurrentCE == NULL) GiveError("AddCurrentCE(): couldn't be found corresponding NewCurrentCE()");
	if(circuit == NULL)
		circuit = CurrentCE;
	else
	{
		CircuitElement *ce_pick = circuit;
		for(unsigned int i=1; i<nof_elements; i++) ce_pick = ce_pick->GetNext();
		ce_pick->SetNext(CurrentCE);
		CurrentCE->SetPrev(ce_pick);
		CurrentCE->SetNext(circuit->GetNext());//maintain the circular doubly linked list.(source level is excluded)
		circuit->GetNext()->SetPrev(CurrentCE);
	}
	nof_elements ++;
	GiveCompMsg("CE %s:%s is added into circuit",CEnames[CurrentCE->GetType()], CurrentCE->GetName());
	CurrentCE = NULL;
}

void CECircuit::DeleteCurrentCE(void)
{
	if(CurrentCE != NULL)
	{
	delete CurrentCE;
	CurrentCE = NULL;
	}
}

void CECircuit::RemoveCE(CircuitElement *CE)
{
	if(CE == NULL) GiveError("Removing NULL CE is not possible! Removing nothing?, you must be kidding :)");
	switch(CE->GetType())
	{
	case cetCABLE:
		if(!CE->CEInfo[ceqHover1])
		{
			CE->V.output = 0; //select first leg.
			theBreadBoard->UnPlugCE(CE);//unplug the leg.
			CE->CEInfo.SetIndex(ceqHover1);
		}
		if(!CE->CEInfo[ceqHover2])
		{
            CE->V.output = 1;
			theBreadBoard->UnPlugCE(CE);
			CE->CEInfo.SetIndex(ceqHover2);
		}
		break;

	case cetLED:
		if(!CE->CEInfo[ceqHover])
		{
			theBreadBoard->UnPlugCE(CE);
			CE->CEInfo.SetIndex(ceqHover);
		}
		break;
	default : return;
	}

	CE->GetPrev()->SetNext(CE->GetNext());
	CE->GetNext()->SetPrev(CE->GetPrev());
	if(CE == circuit->GetNext()) circuit->SetNext(CE->GetNext());
	delete CE;
	nof_elements--;
	if(nof_elements == 1) circuit->SetNext(NULL);
}

void CECircuit::SaveCircuit(void)
{			
	BB_BUF bb_buf;
	CEH_BUF ceh_buf;
	GH_BUF gh_buf;	

	int CEC_file;
	unsigned int ce_count,i,creation_no;	
	CircuitElement *pick_CE;
	Gate *pick_Gate, *found_Gate;
	unsigned short *CableHeight; //for cable height to be overwritten onto the name field.
	
	//GIVE CEC EXTENSION TO FILE NAME
	char *extension;
	for(extension = ScriptFilePath + strlen(ScriptFilePath) - 1 ; *extension != '.'; extension--)
	{
		if(*extension == '\\' || extension == ScriptFilePath)
		{
            extension = ScriptFilePath + strlen(ScriptFilePath);
			break;
		}
	}
	strcpy(extension, ".cec");
	//eof GIVING EXTENSION


	//initialize the file. Corresponding CESL file gets the CEC extension
	CEC_file = _creat(ScriptFilePath, _S_IREAD | _S_IWRITE); //create or if exists truncate the file.
	

	if(CEC_file == -1)
	{
		perror("Error while creating the CEC file");
        exit(1);
	}
	_setmode(CEC_file,_O_BINARY);

	//write CEC file Identification
	write(CEC_file, "CEC_FILE:", 9);

	//Write Last Cam Location
	if(_write(CEC_file, &camcone, sizeof(CamCone)) < sizeof(CamCone))
	{
		_unlink(ScriptFilePath);
		GiveError("CEC file save error while writing Last Camera Location: %s",0,ScriptFilePath);
	}	

	//Write BB (BreadBoard) information
	bb_buf.rows = CurrentBBrow;
	bb_buf.cols = CurrentBBcol;
	bb_buf.nof_holes_in_a_node = CurrentBBstride;

	if(_write(CEC_file, &bb_buf, sizeof(BB_BUF)) < sizeof(BB_BUF))
	{
		_unlink(ScriptFilePath);
		GiveError("CEC file save error while writing BB information: %s",0,ScriptFilePath);
	}
	//eof BB information

	//write the last LastSelectedCE
		//search the list order of the last LastSelectedCE.
		for(ce_count = 0, pick_CE = circuit->GetNext(); pick_CE != LastSelectedCE; pick_CE = pick_CE->GetNext(),ce_count++);
		if(_write(CEC_file, &ce_count, sizeof(unsigned int)) < sizeof(unsigned int))
		{
			_unlink(ScriptFilePath);
			GiveError("CEC file save error while writing Last LastSelectedCE list order: %s",0,ScriptFilePath);
		}
	//eof write last LastSelectedCE.

	//Write the Circuit
	for(ce_count = 0,pick_CE = circuit->GetNext(); ce_count < nof_elements-1; ce_count++,pick_CE = pick_CE->GetNext())
	{//for each circuit element		

		//get information about the ce.
		ceh_buf.type = pick_CE->GetType();
		ceh_buf.CEInfo = pick_CE->CEInfo.GetByte(0);
		strcpy( ceh_buf.name, pick_CE->GetName());		
		ceh_buf.V_leg_no = pick_CE->V.leg_no;
		ceh_buf.G_leg_no = pick_CE->G.leg_no;
		ceh_buf.Position_X1 = pick_CE->GetPositionX();
		ceh_buf.Position_Y1 = pick_CE->GetPositionY();
		ceh_buf.nof_inputs = pick_CE->GetPositionX2();
		ceh_buf.nof_outputs = pick_CE->GetPositionY2();
		ceh_buf.nof_gates = pick_CE->GetNofGates();
		//eof ce header information
		
		if(pick_CE->GetType() == cetCABLE)
		{//for cables save the cable height info(CE->V.nof_inputs) into the name field.
            CableHeight = (unsigned short *) ceh_buf.name; 
			*CableHeight = pick_CE->V.nof_inputs;
		}

		//write the CEH (Circuit Element Header)
        if(_write(CEC_file, &ceh_buf, sizeof(CEH_BUF)) < sizeof(CEH_BUF))
		{
			_unlink(ScriptFilePath);
			GiveError("CEC file save error while writing CE Header: %s",0,ScriptFilePath);
		}		

		//Write GL (Gate List) (KIMT: for CABLE and LED, the gate head is NULL)
		for(pick_Gate = pick_CE->GetHead(); pick_Gate != NULL; pick_Gate = pick_Gate->GetNext())
		{//for each gate in this circuit element.

            gh_buf.leg_no = pick_Gate->GetLegNo();
			gh_buf.type = pick_Gate->GetGateType();
			gh_buf.nof_inputs = gh_buf.type == gtInput ? 0 : pick_Gate->GetNofInputs(); //dont save (CL) for gtInput.

			//Write GH (Gate Header)
			if(_write(CEC_file, &gh_buf, sizeof(GH_BUF)) < sizeof(GH_BUF))
			{
				_unlink(ScriptFilePath);
				GiveError("CEC file save error while writing the Gate Header: %s",0,ScriptFilePath);
			}					

			//Write CL (Connection List) (KIMT: for gtInput the (CL) is not saved)
			for(i=0 ; i < gh_buf.nof_inputs ; i++)
			{//for each connection write creation order of the connected gate.
				
				//find the list_order of the gate which gives output to this gate.
                for(creation_no=0, found_Gate = pick_CE->GetHead() ; found_Gate!=NULL; found_Gate = found_Gate->GetNext(), creation_no++)
					if(pick_Gate->Input[i] == found_Gate) //there is no possibility of a gate not to be connected to some other gate,
						break;								//so, it is not necessary to check the case if the gate not found in the list.

				//write the list order of the gate which gives output to this gate.
				if(_write(CEC_file, &creation_no, sizeof(unsigned int)) < sizeof(unsigned int))
				{
					_unlink(ScriptFilePath);
					GiveError("CEC file save error while writing the Gate Connection List: %s",0,ScriptFilePath);
				}
			}//eof CL
			
		}//eof GL

	}//eof for each CE.



	if(_commit(CEC_file) == -1)
	{//be sure the information is written in the CEC file else remove the damaged file.		
		GiveError("CEC file save error: %s",0,ScriptFilePath);
        _unlink(ScriptFilePath);
	}
	
	_close(CEC_file);
}

void CECircuit::LoadCircuit(char *file_path)
{	
	BB_BUF bb_buf; //breadboard buffer.
	CEH_BUF ceh_buf; //circuit element header buffer.
	GH_BUF gh_buf; //gate header buffer.
	char CEC_file_id[9];
	unsigned int count_gates;
	unsigned int count_connections;
	unsigned int LastSelectedCEListOrder;
	unsigned int list_order, j, i;
	Gate *connect_gate, *pick_GATE;

	SetCamCone = true; //tell the Artist that set the new Camcone.

	int CEC_file;

	GiveCompMsg("File is reading now (%s)...",file_path);

    CEC_file = _open(file_path, _O_RDONLY | _O_BINARY);

	if(CEC_file == -1)
	{
		perror("CEC file Error:");
        GiveError("CEC file open error occured");
	}

	//check CEC file identification
	_read(CEC_file, &CEC_file_id, 9);
	if(strncmp("CEC_FILE:", CEC_file_id, 9))
		GiveError("CE_FILE id confliction: This file is not a Circuit Engine Circuit file or it has been corrupted: %s",0,file_path);
	//eof checking CEC file identification

	GiveCompMsg("File ID is OK, the Circuit is Loading...");

	//Get Last Cam view
	_read(CEC_file, &NewCamCone, sizeof(CamCone));

	GiveCompMsg("Setting up the BreadBoard information...");

    //Get BreadBoard Specification and set BB.
	_read(CEC_file, &bb_buf, sizeof(bb_buf));
	theBreadBoard = new BreadBoard(CurrentBBrow = bb_buf.rows,	CurrentBBcol = bb_buf.cols, CurrentBBstride = bb_buf.nof_holes_in_a_node);
	//eof BB specification loading.

	GiveCompMsg("BreadBoard is ready.");

	//Get the Last LastSelectedCE list order
	_read(CEC_file, &LastSelectedCEListOrder, sizeof(unsigned int));

	//theBreadBoard->debugInsertVG(circuit->GetHead(), circuit->GetHead()->GetNext()); // DEBUG

	//Get Circuit
	//for each circuit element (until eof)
	for(;!_eof(CEC_file);)
	{
		//get Circuit Element Header (CEH)
		_read(CEC_file, &ceh_buf, sizeof(ceh_buf));

		NewCurrentCE();

		CurrentCE->SetType(ceh_buf.type);
		CurrentCE->CEInfo.SetByte(0, ceh_buf.CEInfo);
		CurrentCE->SetName(ceh_buf.name);
		CurrentCE->SetVoltageLegNo(ceh_buf.V_leg_no);
		CurrentCE->SetGroundLegNo(ceh_buf.G_leg_no);
		CurrentCE->MoveTo(ceh_buf.Position_X1,ceh_buf.Position_Y1);
		CurrentCE->SetNofInputs(ceh_buf.nof_inputs); //position X2 for CABLE
		CurrentCE->SetNofOutputs(ceh_buf.nof_outputs); //position Y2 for CABLE

		if(ceh_buf.type == cetCABLE) 
		{// (cable specific settings)
			CurrentCE->V.next_gate = (Gate *)(CurrentCE);
			CurrentCE->V.SetType(gtCable); 
			CurrentCE->V.leg_no = CurrentCE->GetID(); //cable node id setting.
			CurrentCE->V.Input[0] = NULL;
			CurrentCE->CEInfo.ResetIndex(ceqHasOd1);
			CurrentCE->CEInfo.ResetIndex(ceqHasOd2);
			CurrentCE->V.nof_inputs = *((unsigned short *)ceh_buf.name); // retrieve the cable height
			CurrentCE->SetName("CABLE"); // repare the name.
		}

		//Get Gate List (GL)
		for(count_gates=0; count_gates < ceh_buf.nof_gates; count_gates++)
		{
            //Get Gate Head (GH)
			_read(CEC_file, &gh_buf, sizeof(gh_buf));
			AddGate(gh_buf.type, gh_buf.type == gtInput ? 1 : gh_buf.nof_inputs, gh_buf.leg_no);
			//eof (GH)

			//Set up connection list of the Gate (CL) (KIMT: for gtInput, this 'for' is not processed)
			for(count_connections = 0 ; count_connections < gh_buf.nof_inputs ; count_connections++)
			{
				_read(CEC_file, &list_order, sizeof(int));
				CurrentGate->Input[count_connections] = (Gate *) list_order;
			}
			//eof (CL)

		}
		//eof (GL)

		//CONVERTING PROCESS (list-order to in-memory-gate-address)					
		for(pick_GATE = CurrentCE->GetHead() ; pick_GATE!=NULL ; pick_GATE = pick_GATE->GetNext())
		{
            if(pick_GATE->GetGateType() == gtInput) continue; //ignore input gate connection (it'll be done in CEngine)
			for(i=0; i<pick_GATE->GetNofInputs(); i++)
			{//connect corresponding input array element which it points to by its list_order inside.
                //proceed onto the specified gate by the list_order.
				for(j=0,connect_gate = CurrentCE->GetHead(); j<(unsigned int)pick_GATE->Input[i] ; ++j,connect_gate = connect_gate->GetNext());
				//now the connect_gate is on the gate which is to be connected to pick_GATE->Input[i].
				pick_GATE->Input[i] = connect_gate;
			}

		}
		//eof CONVERTING PROCESS.

		//LegPlugability binary array creation for the CE.
		switch(CurrentCE->GetType())
		{
		case cetCABLE:
		case cetLED:
			CurrentCE->LegPlugAbility.CreateBitArray(2);
			break;
		case cetCHIP:
			CurrentCE->LegPlugAbility.CreateBitArray( ceh_buf.nof_inputs + ceh_buf.nof_outputs + 2 + ((ceh_buf.nof_inputs+ceh_buf.nof_outputs)%2) );
			break;
		}	
		//eof LegPlugability

		AddCurrentCE();
	}//eof Circuit Element

	GiveCompMsg("In-CircuitElement connections completed, printing retrieved circuit (%s)",file_path);
	PrintCurrentCircuit();

	_close(CEC_file);

	//Set the last selected CE.
	CircuitElement *pick_CE = circuit->GetNext(); // pass source level.

	for(i=0; i<LastSelectedCEListOrder; i++, pick_CE = pick_CE->GetNext());
	LastSelectedCE = pick_CE;

	InsertTempVG();//TEST (upper left two bit)
	//(RE)PLUGGING PROCESS
    //Check All CEs if they were plugged, if so, make them plugged again for the session.
	GiveCompMsg("(Re)plugging process is initiated (%s)",file_path);
	
	pick_CE = circuit->GetNext(); // pass source level.
	bool OtherHover; //for maintenance of corresponding cable nodes.

	for(i = 0; i<nof_elements-1; i++, pick_CE = pick_CE->GetNext())
	{
		GiveCompMsg("Plugging %s",pick_CE->GetName());
		theBreadBoard->SetLegPlugAbility(pick_CE);
		

		switch(pick_CE->GetType())
		{
		case cetCABLE:			

			OtherHover = pick_CE->CEInfo[ceqHover2];
			pick_CE->CEInfo.SetIndex(ceqHover2); //stay uplugged while first leg is plugging.			

			pick_CE->CEInfo.ResetIndex(ceqHasOd1);
			pick_CE->CEInfo.ResetIndex(ceqHasOd2);			

			if(pick_CE->CEInfo[ceqHover1] == false) // if first leg is plugged
			{
				pick_CE->V.output = 0; //select leg 1
				pick_CE->CEInfo.SetIndex(ceqHover1);
				theBreadBoard->PlugCE(pick_CE);
			}
			if(OtherHover == false) // if second leg is plugged
			{				
				pick_CE->V.output = 1; //select leg 2				
				theBreadBoard->PlugCE(pick_CE);
			}

			break;

		case cetCHIP:			
		case cetLED:

			if(pick_CE->CEInfo[ceqHover] == false)//if it was plugged
			{//do real plug operation.
				theBreadBoard->PlugCE(pick_CE);
			}

			break;
		}
	}
	GiveCompMsg("(Re)plugging process is completed");
	//EOF (RE)PLUGGING PROCESS

	GiveCompMsg("Circuit is printing after (replugging process)");
	PrintCurrentCircuit();

}
void CECircuit::InsertTempVG(void)
{
	theBreadBoard->debugInsertVG(circuit->GetHead(),circuit->GetHead()->GetNext());
}

void CECircuit::SetLegPlugAbilityOfCurrentCE (void)
{
    theBreadBoard->SetLegPlugAbility(CurrentCE);
	return;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Engineer Siemens
Turkey Turkey
I've graduated from computer engineering department in 2004 July, and developed the Circuit Engine as my graduation project at Eastern Mediterranean University (EMU). I've also graduated from MBA, Istanbul University in 2008 February. From 2004 until now, I've developed many web sites (B2B, B2C). Currently work for Siemens AG (http://siemens.com/ingenuityforlife) as a R&D Engineer (IoT) and I still continue to develop my personal projects about encryption algorithms, maths, OpenGL, Stock Exchange Market Analyses and Real-time Systems, MultiThreaded Applications and Web Solutions (mostly ASP.NET C#, JQuery/JS, MVC, WinForms, Win/Web Services, T-SQL and less PHP, ASP, MySQL and some possible other methods about problem solving if necessary). Also personal profession of photography : https://www.instagram.com/egldgn/


Circuit Engine base URL : https://sites.google.com/view/circuitengine


Comments and Discussions