I've a question, in my main agent I've some variables which I want to export to a excel row based on a parameter value (in this case AGVs) after the simulation duration. Now I want to vary this parameter by using the parameter variation experiment, but somehow it does not work and only updates the excel sheet for the last run. So in the figure below you see the exportdata function which is the function which puts the data in the excel sheet columns.
Now I want to vary a parameter in this example I choose the number of AGVs, so I created an parameters variation experiment and set this number of AGVs in a specified range see figure below:
Now I want for every parameter the ExportData function to run, and put it in new rows for every AGV value:
AGV=13 ExcelRow=1 ExportData()
AGV=14 ExcelRow=2 ExportData()
....
So I tried to do this by putting the variable NrOfExperiment in the ParametersVariation agent and let it do +1 after every run see the next figure:
But somehow he only export the data for the last run instead of all the runs? How can I fix this problem? I already tried to put it in after iteration by java actions but this would not work.
Thanks!
Easiest option is to just write all model results into a dbase table at the end, with an extra iteration and replication column (fill with getExperiment().getCurrentIteration/Replication()).
Write to that at the end of each model (on destroy of Main).
At the end of the experiment, simply setup the dbase to export to an Excel file.
Related
I'm already struggling for days to use dates from excel in a proper way in anylogic..
I've created a database in where the date is formulated as integers in different columns since otherwise excel is messing up the dates (for example year=2021 , month=12 day=5 hour=6 minute=44 second=0 stands for 2021/12/5 6:44:00)
Now I know this can be converted to a date by the function toDate(year, month, day, hour, minutes seconds). But how can I use this integers to create agent with specific parameters from the database in a source and add to a custom population?
The most simple way is to add a column where the function toDate(......) is added in the database but I do not know how to do this (see picture if it is unclear). Or are there other solutions?
One way: use Dynamic Events.
Create one and in the action code, write mySource.inject(1)
In Main, on startup, load all dbase rows and create a DE for each row, below assuming it is only with an hour-column:
(Use the database query wizard to adjust your query).
In your source object, set it to "call of inject() function"
This will work, but it is quite cumbersome, as you can see. Much easier if you get your Excel right and just import the date column clean and well so you can use the Source option "arrival table in database" directly. I know you need regular arrivals, so maybe code that up in Excel to give you these on specific dates...
I am simulating a medical stay and would like to calculate for each patient generated the cost of their stay. I have created a parameter that is updated each time the patient receives care. However, to plot it, I created a histogram data with a cyclic event that updates the values every hour with the following code:
this.CostperPatient.reset();
for (Patient patients: this.patients)
{
this.CostperPatient.add(patients.cost);
}
However, the chart it produces does not give an indication of individual patients, it makes a percentage.
Is there any way to output a graph with the cost for each individual patient?
Thank U
Miriana
While I am not sure what you are trying to show, it seems that in your case, you shouldn't be using a histogram, instead you should probably use a bar chart where each bar is for a patient. To do so, drag to your model in the main environment a bar chart element and call it chart. To dynamically add bars to this element, you need to use something called "data items". For now, remove all data items from the chart by clicking on the X in the properties.
Then, go to the Agent window, so your patients' agent window. Add a variable. Set it as follows:
Then, in the agent's properties, in the on startup field write the following:
Finally, add a cyclic event in the agent's window with the following code:
patientCost.setValue(cost);
Where cost in the above, refers to your initial variable called cost in the question. That should do it.
I have an excel sheet with many tabs. Say one is called wsMain and the other is called wsDate.
In my data flow transformation I am able to successfully load the data from wsMain to my table.
Now I have to update this transformation where I have to fetch the maximum date from the worksheet wsDate and only load data from wsMain where the date is less than on equal to the maximum date in wsDate (that is the only column available).
So for I have figured out that I need to create a new Excel connection manager to read the data from wsDate and I have used the Aggregate transformatioin to get the maximum date.
Now the question is how do I use this date to restrict the rows coming from wsMain?
I understand from the link below that you can store the value in a variable but what do I do next?:
SSIS set result set from data flow to variable
I have tried using a merge join but not sure if I am doing it right.
Here is what it looks like now:
I could not achieve the above but would be interested to know if that is possible. As a work around I have created a separate dataflow where I have stored the valued in a variable and then used the variable in the conditional split to filter the required rows:
Here is a step by step guide I followed to write the variable:
https://www.proteanit.com/2008/12/11/ssis-writing-to-a-package-variable-in-a-dataflow/
You can obtain the maximum value of the wsDate column first, this use this as a filter to avoid introducing unnecessary records into the data flow which which would be discarded by the Conditional Split. An overview of this process is below. I'd also recommend confirming the data types for all columns involved.
Create an SSIS DateTime variable and name this something descriptive such as MaxDate.
Create a Data Flow Task before the current one with an Excel Source component. Use the SQL command option for the Data Access Mode and enter a SQL statement to return the max value of the wsDate column. In the following example ExcelSource is the name of the sheet that you're pulling from. I'd suggested confirming the query with the Preview button on the Excel Source as well.
Add a Script Component (not Task) after the Excel Source. Add the MaxDate variable in the ReadWriteVariables field on the main page of the Script Component. On the Inputs and Outputs pane add the output column from the Excel Source as an Input Column with the ReadOnly usage Type. Example C# code for this is below. Note that variables can only be written to in the PostExecute method. The Input0_ProcessInputRow method is called once for each row that passes through, however there will only be the single row in this case. On the following code MaxExcelDate is the name of the output column from the Excel Source.
On the Excel Source component in the Data Flow Task where the records are imported from Excel, change the Data Access Mode to SQL command and enter a SQL statement to return records that have a date less than or equal to the maximum wsDate value. This is the last example and the ? is a placeholder for the parameter. After entering this SQL, click the Parameters button and select Parameter0 for the Parameters field, the MaxDate variable for Variables field, and a direction of Input. The Conditional Split can then be removed since these records will now be filtered out.
Excel MAX wsDate SELECT:
SELECT MAX(wsDate) AS MaxExcelDate FROM ExcelSource
C# Script Component:
DateTime maxDate;
public override void PostExecute()
{
base.PostExecute();
Variables.MaxDate = maxDate;
}
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
maxDate = Row.MaxExcelDate;
}
Excel Command with Date Filter:
SELECT
Column1,
Column2,
Column3
FROM ExcelSheet
WHERE DateColumn <= ?
Yes, it is possible. In the data flow, you will need to determine the max date, which you already have. Next, you will need to MERGE JOIN the two data flows on the date column. From there, you will feed it into a CONDITIONAL SPLIT and split where the date columns match [i.e., !ISNULL()] versus do not match [i.e., ISNULL()]. In your case, you only want the matches. The non-matches will be disregarded.
Note: if you use an INNER JOIN on the MERGE JOIN where there is only one date (i.e., MaxDate) to join on, then this will take care of the row filtering for you. You will not need a CONDITIONAL SPLIT.
Welcome to ETL.
Update
It is a real pain that SSIS's MERGE JOINs only perform joins on EQUAL operations as opposed to LESS THAN and GREATER THAN operations. You will need to separate the data flows.
Use a script component to scan the excel file for the MAX Date and assign that value to a package variable in SSIS. Alternatively, you can have a dates table in SQL Server and then use an Execute SQL Command in SSIS to retrieve the MAX Date from the table and assign that value to a package variable
Modify your existing data flow to remove the reading of the Excel date file completely. Then add a DERIVED COLUMN transformation and add a new column that is mapped to the package variable in SSIS that stores the MAX date. You can name the Derived Column Name 'MaxDate'
Add a conditional split transformation with the following CONDITION logic: [AsOfDt] <= [MaxDate]
Set the Output Name to Insert Records
Note: The CONDITIONAL SPLIT creates a new output data flow with restricted/filtered rows. It does not create a new column within the existing data flow. Think of this as a transposition of data flow output from column modification to row modification. Only those rows that match the condition will be sent to the output that you desire. I assume you only want to Insert these records, so I named it that. You can choose whatever naming convention you prefer
Note 2: Sorry for not making the Update my original answer - I haven't used the AGGREGATE transformation before so I was not aware that it restricts row output as opposed to reading a value in the data flow and then assigning it to a variable. That would be a terrific transformation for Microsoft to add to SSIS. It appears that the ROWCOUNT and SCRIPT COMPONENT transformations are the only ones that have the ability to set a package variable value within the data flow.
In my main report I get a (small) list of string values from the data base. I then want to use this list for selecting records in a subreport, along with other input parameters:
The user shall be able to select records based on a range of begin and end date -- this is easy using an input parameter of type java.util.Date with "Is For Prompting" set to true. Another criteria shall be one or more items from a list showing values from a data base field. I could define the list in the report template, but then I'd have hard-coded strings (filled from the data base, but at definition time only).
Now the dilemma is: If I define the input parameters in the main report, I cannot get the values for the list beforehand; if I define them in the subreport, I get no prompt at all, so there's no way to set any of them.
So the report requires values for start and end date, and a list of string values to select from (multiple itmes can be selected). This list shall be built from values from the data base. In the subreport all these values shall be joined into a filter for the records. A user shall be able to define the dates and select items from the list manually before executing the report.
Is there a way to achieve this?
After some more hours of trial & error, and some more research, of course, I found that the keyword is "Query-based Input Controls". This documentation describes their creation on the JasperReports Server. Such input controls can be edited in Jaspersoft Studio as well, however, they actually work on the server only. Anyway, this is the solution to my problem.
Having some problems in a job, and I suspect it is due to a lack of understanding of tJavaFlex. I am generating 10 rows in this test job, and am generating loop inside a tJavaFlex:
So there are 10 rows coming in, and a loop in the Start and End section. I was expecting that for each row coming in, it would generate 10 identical rows coming out. And that I would see iterations 0,1,2,3....9 for each row.
What I got was this. This looks to me like the entire job is running 10 times, and so I have 100 random values coming through the flow from the tRowGenerator.
If I move the for loop into the Main Code section, I get close to the behaviour I was expecting. I am expecting each row when it comes in to be repeated 10 times, and for 1 row coming in to produce 10 output rows. What I get is this.
But even then my tLogRow is only generating one row for each 10 iterations it seems (look at the tLogRow output after iteration 9 above why not 10 items?). I had thought I would be getting 10 rows for each single row coming in and I would see this in the tLogRow.
What I need to do is take a value from a field coming in, do some reg exp parsing and split into an array, and then for each item in the array create lines in the output flow. i.e. 1 row coming in can be turned into x number of rows coming out using a string.split() method.
Can someone explain the behaviour above, and also advise on the best approach to get one value coming in, do some java manipulation and then generate multiple rows coming out?
Any advice appreciated.
Yes you don't use it correctly.
The initial part is for initiate variable. (executed one time before the first tow)
In the principal you put your loop (executed one time at each row)
In the final you store in global variable for example.(executed one time after the last row)
The principal code will be executed at each row in a tjavaflex. So don't put a for loop inside you can do like the example in the screen.
You tjavaflex comportement is normal. you have ten row so each row the for loop wil be executed 10 time (i<10)
You can use it like :
You dont need to create your own loop.
By putting the for loop in the Start code, your main code will be triggered by the loop and by incoming rows, and it will be executed n*r times.
The behaviour of subjob that contains a tJavaFlex, reveils that component before tJavaFlex is included into its starting code, and the after component is included in the ending code, but that may depend to many conditions like data propagation and trigger type.
start code :
System.out.print("tJavaFlex is starting...");
int i = 0;
Main code :
i++;
System.out.print("tJavaFlex inside Main Code...iteration:"+i);
row8.ITEM_NAME = row7.ITEM_NAME;
row8.ITEM_COUNT = row7.ITEM_COUNT;
End code :
System.out.print("tJavaFlex is ending...");
System.out.print(row7.ITEM_NAME);
Instead of main flow in row5, try using iterate flow to connect tJavaFlex