I tried to create a package of cars but when I invoke transport chaincode to change the owner of the cars in the package, its owner wasn't changed.
https://github.com/JackAtlas10/Learing-Fabric/blob/master/Learn%20GoLang/Go%20L/tempContract.go
There are several issues in your code.
In 'InitPackage' method, you are adding empty array of cars (you should add array of car inside it)
In 'Transport' method, the for loop is not correct. You are executing loop on a string variable, 'cNum'. According to your code, whenever you will call 'AddCarsToPkg' method, the 'cNum' variable of the package store a new value (or will not reserves the old car numbers)
I have solution in my mind. It may be helpful for you.
That is:
Add 'cNum' as a element of 'Car' structure & remove this element from 'Packages' struct
before 'PutState' also set the value of Car element 'cNum'
In 'Transport' method, replace for loop with below given code
for i := range tempPkg.cars {
carIndex := tempPkg.cars[i].cNum
car, _ := s.QueryCar(ctx, carIndex)
car.Owner = newOwner
car.Status = "Handed Over"
carAsBytes, _ := json.Marshal(*car)
err := ctx.GetStub().PutState(carIndex, carAsBytes)
if err != nil {
return fmt.Errorf("Error in Puting state to data base%s", err.Error())
}
tempPkg.cars[i].Owner = newOwner
tempPkg.cars[i].Status = "Handed Over"
}
Related
I am iterating over a collection of data, in my case, an array of objects. Here is a sample of 2 data points from it:
{
violation_id: '211315',
inspection_id: '268804',
violation_category: 'Garbage and Refuse',
violation_date: '2012-03-22 0:00',
violation_date_closed: '',
violation_type: 'Refuse Accumulation' },
{
violation_id: '214351',
inspection_id: '273183',
violation_category: 'Building Conditions',
violation_date: '2012-05-01 0:00',
violation_date_closed: '2012-04-17 0:00',
violation_type: 'Mold or Mildew' }
I need to create a new array of objects from this, one for each "violation_category" property. If Violation category already exists in the new array I am creating, i simply add the information to that existing category object (instead of having two "building conditions" objects for example, I would just add to an existing one).
However, I am having trouble assigning to the existing object if the current one exists (it's easy to check if it does not, but not the other way around). This is what am attempting to do currently:
if (violationCategory.uniqueCategoryName) {
violationCategory.uniqueCategoryName.violations = results[i].violation_id;
violationCategory.uniqueCategoryName.date = results[i].violation_date;
violationCategory.uniqueCategoryName.closed =
results[i].violation_date_closed;
} else {
category.violations = results[i].violation_id;
category.date = results[i].violation_date;
category.closed = results[i].violation_date_closed;
violationCategory.push(category);
}
In first condition, if this category (key) exists, I simply add to it, and in the second condition, this is where I am struggling. Any help appreciated. Thanks guys.
Just add an empty object to the key if there no object there :
violationCategory.uniqueCategoryName = violationCategory.uniqueCategoryName || {};
And only then, add the data you want to the object.
violationCategory.uniqueCategoryName.violations = results[i].violation_id;
violationCategory.uniqueCategoryName.date = results[i].violation_date;
violationCategory.uniqueCategoryName.closed =
results[i].violation_date_closed;
No condition needed.
Good luck!
Assuming that you have an input variable which is an array of objects, where the objects are looking like the objects of the question, you can generate your output like this:
var output = {};
for (var item of input) {
if (!output[item.violation_category]) output[item.violation_category] = [];
output[item.violation_category].push(item);
}
Of course you might customize it like you want.
This is for a simple script I'm writing for an oldschool MUD game for those who know what that is.
Basically I am searching through one table (which I'm reading from gmcp) and trying to search the value's on that table to see if any of them match any of the value's on another table which I'm storing the value's I'm looking for.
I've successfully managed to do it with singular value's by simply using a "for" loop to grab the value from gmcp and store it as a variable and then using another "for" loop to search the other table to see if any of the values there match the variable.
Trouble is, it only works for a singular value and misses all the others if there is more than one value I need to check in that table.
The code I have is as follows,
for _, v in pairs(gmcptable) do
checkvalue = v
end
for _, v in pairs(mytable) do
if v == checkvalue then
echo("yay")
else
echo("no!")
end
end
again this works fine for gmcp tables with one value, but fails if more. I tried doing this to,
for _, v in pairs(gmcptable) do
checkvalue = v
for _, v in pairs(mytable) do
if v == checkvalue then
echo("yay")
else
echo("no!")
end
end
end
my hope was that it might set the variable, run the second for loop to check the variable and then repeat for the next value on the gmcp table since it's a for loop and the second loop was within the loop, but that didn't work either. I also tried making my own function to add to the mix and simplify it,
function searchtable(table, element)
for _, v in pairs(table) do
if v == element then
return true
else
return false
end
end
end
for _, v in pairs(gmcptable) do
if searchtable(mytable, v) == true then
echo("yay")
else
echo("no!")
end
end
that was a bust also... I'm sure I'm just overlooking something or showing what an amateur I am, but I've googled loads and tried everything I can think of, but I'm just self taught and only recently started understanding how tables and for loops even work. Hopefully someone out there can get back to me with something that works soonish!
UPDATE!
#Piglet Okay so, gmcptable was actually me trying to simplify the question for those who could answer the coding question. gmcptable actually is a long list of tables received by my client via the connection from the server the game this is for. so in all actuality, I have 3 tables I'm parsing data from. "gmcp.Char.Items.List.items", "gmcp.Char.Items.Add" and "gmcp.Char.Items.Remove". Now gmcp.Char.Items.List.items is the list of everything in the room I'm in within the game. gmcp.Char.Items.Add is the list of anything that enter the room and is sent each time anything enters the room aside from other players and gmcp.Char.Items.Remove is the same, but for when anything leaves the room. I'm trying to use this information to create a targeting table that will automatically add desired targets to my targeting que and remove them if they are not in the room. the room list (gmcp.Char.Items.List) is updated only when I enter or exit the room and possibly when I look, but for now I'm assuming it doesn't update when I look because that will be a whole other problem to solve later.
I currently have a simple script in what my client ID's as a trigger, this is set to fire once when I log into the game in question and the script define the tables that hold the value's I'm cross referencing the gmcp tables with to figure out if it's information I want added to my target table, this script also defines the target table as empty, which is meant to ensure that for the duration of the session, both tables exist and are defined.
I then added three separate scripts that parse the three gmcp tables and figure out whether that are on my desired targets table and if so adds it or in the of the case of the remove table checks if its currently on the targets table and if so removes it. below I'll show the current scripts I'm using (which have changed several times over since yesterday and might change again before I get a look at any future replies to this. I will also include a what the gmcp tables in question look like and if I'm currently seeing any error or debug details from my client I'll include that as well.
log on trigger
match on > ^Password correct\. in perl regex
bashtargets = {}
bashlist = {
"a baby rat",
"a young rat",
"a rat",
"an old rat",
"a black rat"
}
(the above trigger appears to be working properly and I can print the tables accurately)
script in the room
event handlers > gmcp.Char.Items.List
for _, v in pairs(gmcp.Char.Items.List.items) do
bashname = v.name
bashid = v.id
for _, v in pairs(bashlist) do
if v == bashname then
table.insert(bashtargets, bashid)
end
end
end
script addcheck
event handlers "gmcp.Char.Items.Add"
for _, v in pairs(gmcp.Char.Items.Add) do
addname = v.name
addid = v.id
for _, v in pairs(bashlist) do
if v == addname then
table.insert(bashtargets, addid)
end
end
end
script removecheck
event handlers "gmcp.Char.Items.Remove"
for _, v in pairs(gmcp.Char.Items.Remove) do
delid = v.id
for _, v in pairs(bashtargets) do
if v == delid then
table.remove(bashtargets, delid)
end
end
end
gmcp table "gmcp.Char.Items"
{
Remove = {
location = "room",
item = {
id = "150558",
name = "a filthy gutter mutt",
attrib = "m"
}
},
Add = {
location = "room",
item = {
id = "150558",
name = "a filthy gutter mutt",
attrib = "m"
}
},
List = {
location = "room",
items = {
{
id = "59689",
name = "a statue of a decaying gargoyle",
icon = "profile"
},
{
id = "84988",
name = "a gas lamp"
},
{
id = "101594",
attrib = "t",
name = "a monolith sigil",
icon = "rune"
},
{
id = "196286",
name = "a wooden sign"
},
{
id = "166410",
name = "Lain, the Lamplighter",
attrib = "m"
}
}
}
}
I have parsed the information successfully several times, so I've got the right tables and syntax and what have you where gmcp is concerned.
using this I have also managed to get it to half work. currently the set up seems to capture single targets at a time even if there are dozens and add that one, sometimes it oddly enough adds the same target 3 - 5 times for some reason, not sure why, haven't been able to figure it out yet.
these two error messages have been output by my client repeatedly, no idea what to do about them though or how to fix them... "left the room" and "entered the room" are the names currently assigned to the scripts for adding and removing data from the tables in my client.
[ERROR:] object:<event handler function> function:<left the room>
<Lua error:[string "return left the room"]:1: '<eof>' expected near 'the'>
[ERROR:] object:<event handler function> function:<entered the room>
<Lua error:[string "return entered the room"]:1: '<eof>' expected near 'the'>
I have no idea what '' means though, or why it's expected near 'the' it's all got my head pounding though...
I can see through the debug feature on my client that all the handlers are being sent by the server so it's not the gmcp... I'm not actually seeing any bugs on the debug feature (which btw is separate from the error feature that keeps putting out those other two errors I mentions.
anyways that's my update... Hopefully that give some people a better handle on what I'm doing wrong so I can get this figured out and learn something new.
Thanks again in advance and extra thanks to you #Piglet for you answer I definitely learned something new from it and thought it was very helpful.
In your first attempt you have 2 separate loops. You overwrite checkvalue for every element in gmcptable. Once you enter your second loop checkvalue will have the value last asigned in your first loop. So you only have 1 checkvalue and you only run across your table once as you only run your second loop once.
for _, v in pairs(gmcptable) do
checkvalue = v
end
for _, v in pairs(mytable) do
if v == checkvalue then
echo("yay")
else
echo("no!")
end
end
Your second attempt should work if I understood your problem.
You iterate over every element of gmcptable and compare it to every element in mytable. So whenever gmcptable contains a value that is also contained in mytable you should get a "yay".
for _, v in pairs(gmcptable) do
checkvalue = v
for _, v in pairs(mytable) do
if v == checkvalue then
echo("yay")
else
echo("no!")
end
end
end
One remark on your third attempt with a function. You should not call arguments table as you will then have no access to the global table functions inside your function. A call to table.sort for example would result in an error as you will index your local parameter table instead.
Okay so I tinkered with it a bit more tonight and kept getting the same error, eventually I googled the error and came to understand that the error was an end of file error, the client I use uses the name of a script as the name for a function if you don't declare it in the actual script, or at least that is my understanding of it, and so I didn't bother defining the function, and if I added an additional end to the script the client threw up another error because it wasn't defined, so I guess the client adds it's own definition to the function but doesn't add it's own end to the function which created the mix up. So long story short, the solution was to define each of the scripts as a function and add another end to close the function and that fixed the problem.
Thanks again to #Piglet for his answer, it was very helpful and dead accurate too! Thanks mate!
I created an ActivityNode (an Entry) and I can add custom fields with the
setFields(List<Field> newListField)
fonction.
BUT
I am unable to modify these fields. (In this case I try to modify the value of the field named LIBENTITE)
FieldList list = myEntry.getTextFields();
List<Field> updatedList = new ArrayList<Field>();
//I add each old field in the new list, but I modify the field LIBENTITE
for(Field myField : list){
if(myField.getName().equals("LIBENTITE")){
((TextField)myField).setTextSummary("New value");
}
updatedList.add(myField);
}
myEntry.setFields(updatedList);
activityService.updateActivityNode(myEntry);
This code should replace the old list of fields with the new one, but I can't see any change in the custom field LIBENTITE of myEntry in IBM connections.
So I tried to create a new list of fields, not modifying my field but adding a new one :
for(Field myField:list){
if(!myField.getName().equals("LIBENTITE")){
updatedList.add(myField);
}
}
Field newTextField = new TextField("New Value");
newTextField .setFieldName("LIBENTITE");
updatedList.add(newTextField );
And this code is just adding the new field in myEntry. What I see is that the other custom fields did not change and I have now two custom fields named LIBENTITE, one with the old value and the second with the new value, in myEntry.
So I though that maybe if I clear the old list of Fields, and then I add the new one, it would work.
I tried the two fonctions
myEntry.clearFieldsMap();
and
myEntry.remove("LIBENTITE");
but none of them seems to work, I still can't remove a custom field from myEntry using SBT.
Any suggestions ?
I have two suggestions, as I had (or have) similar problems:
If you want to update an existing text field in an activity node, you have to call node.setField(fld) to update the field in the node object.
Code snippet from my working application, where I'm updating a text field containing a (computed) start time:
ActivityNode node = activityService.getActivityNode(id);
node.setTitle(formatTitle()); // add/update start and end time in title
boolean startFound = false;
// ...
FieldList textfields =node.getTextFields();
Iterator<Field> iterFields = textfields.iterator();
while (iterFields.hasNext()) {
TextField fld = (TextField) iterFields.next();
if (fld.getName().equals(Constants.FIELDNAME_STARTTIME)) {
fld.setTextSummary(this.getStartTimeString()); // NOTE: .setFieldValue does *not* work
node.setField(fld); // write updated field back. This seems to be the only way updating fields works
startFound=true;
}
}
If there is no field with that name, I create a new one (that's the reason I'm using the startFound boolean variable).
I think that the node.setField(fld) should do the trick. If not, there might be a way to sidestep the problem:
You have access to the underlying DOM object which was parsed in. You can use this to tweak the DOM object, which finally will be written back to Connections.
I had to use this as there seems to be another nasty bug in the SBT SDK: If you read in a text field which has no value, and write it back, an error will be thrown. Looks like the DOM object misses some required nodes, so you have to create them yourself to avoid the error.
Some code to demonstrate this:
// ....
} else if (null == fld.getTextSummary()) { // a text field without any contents. Which is BAD!
// there is a bug in the SBT API: if we read a field which has no value
// and try to write the node back (even without touching the field) a NullPointerException
// will be thrown. It seems that there is no value node set for the field. We
// can't set a value with fld.setTextSummary(), the error will still be thrown.
// therefore we have to remove the field, and - optionally - we set a defined "empty" value
// to avoid the problem.
// node.remove(fld.getName()); // remove the field -- this does *not* work! At least not for empty fields
// so we have to do it the hard way: we delete the node of the field in the cached dom structure
String fieldName = fld.getName();
DeferredElementNSImpl fldData = (DeferredElementNSImpl) fld.getDataHandler().getData();
fldData.getParentNode().removeChild(fldData); // remove the field from the cached dom structure, therefore delete it
// and create it again, but with a substitute value
Field newEmptyField = new TextField (Constants.FIELD_TEXTFIELD_EMPTY_VALUE); // create a field with a placeholder value
newEmptyField.setFieldName(fieldName);
node.setField(newEmptyField);
}
Hope that helps.
Just so that post does not stay unanswered I write the answer that was in a comment of the initial question :
"currently, there is no solution to this issue, the TextFields are read-only map. we have the issue recorded on github.com/OpenNTF/SocialSDK/issues/1657"
I have records that have an index attribute to maintain their position in relation to each other.
I have a plugin that performs a renumbering operation on these records when the index is changed or new one created. There are specific rules that apply to items that are at the first and last position in the list.
If a new (or existing changed) item is inserted into the middle (not technically the middle...just somewhere between start and end) of the list a renumbering kicks off to make room for the record.
This renumbering process fires in a new execution pipeline...We are updating record D. When I tell record E to change (to make room for D) that of course fires the plugin on update message.
This renumbering is fine until we reach the end of the list where the plugin then gets into a loop with the first business rule that maintains the first and last record differently.
So I am trying to think of ways to pass a flag to the execution context spawned by the renumbering process so the recursion skips the boundary edge business rules if IsRenumbering == true.
My thoughts / ideas:
I have thought of using the Depth check > 1 but that isn't a reliable value as I can't explicitly turn it on or off....it may happen to work but that is not engineering a solid solution that is hoping nothing goes bump. Further a colleague far more knowledgeable than I said that when a workflow calls a plugin the depth value is off and can't be trusted.
All my variables are scoped at the execute level so as to avoid variable pollution at the class level....However if I had a dictionary object, tuple, something at the class level and one value would be the thread id and the other the flag value then perhaps my subsequent execution context could check if the same owning thread id had any values entered.
Any thoughts or other ideas on how to pass context information to a new pipeline would be greatly appreciated.
Per Nicknow sugestion I tried sharedvariables but they seem to be going out of scope...:
First time firing post op:
if (base.Stage == EXrmPluginStepStage.PostOperation)
{
...snip...
foreach (var item in RenumberSet)
{
Context.ParentContext.SharedVariables[recordrenumbering] = "googly";
Entity renumrec = new Entity("abcd") { Id = item.Id };
#region We either add or subtract indexes based upon sortdir
...snip...
renumrec["abc_indexfield"] = TmpIdx + 1;
break;
.....snip.....
#endregion
OrganizationService.Update(renumrec);
}
}
Now we come into Pre-Op of the recursion process kicked off by the above post-op OrganizationService.Update(renumrec); and it seems based upon this check the sharedvariable didn't carry over...???
if (!Context.SharedVariables.Contains(recordrenumbering))
{
//Trace.Trace("Null Set");
//Context.SharedVariables[recordrenumbering] = IsRenumbering;
Context.SharedVariables[recordrenumbering] = "Null Set";
}
throw invalidpluginexception reveals:
Sanity Checks:
Depth : 2
Entity: ...
Message: Update
Stage: PreOperation [20]
User: 065507fe-86df-e311-95fe-00155d050605
Initiating User: 065507fe-86df-e311-95fe-00155d050605
ContextEntityName: ....
ContextParentEntityName: ....
....
IsRenumbering: Null Set
What are you looking for is IExecutionContext.SharedVariables. Whatever you add here is available throughout the entire transaction. Since you'll have child pipelines you'll want to look at the ParentContext for the value. This can all get a little tricky, so be sure to do a lot of testing - I've run into many issues with SharedVariables and looping operations in Dynamics CRM.
Here is some sample (very untested) code to get you started.
public static bool GetIsRenumbering(IPluginExecutionContext pluginContext)
{
var keyName = "IsRenumbering";
var ctx = pluginContext;
while (ctx != null)
{
if (ctx.SharedVariables.Contains(keyName))
{
return (bool)ctx.SharedVariables[keyName];
}
else ctx = ctx.ParentContext;
}
return false;
}
public static void SetIsRenumbering(IPluginExecutionContext pluginContext)
{
var keyName = "IsRenumbering";
var ctx = pluginContext;
ctx.SharedVariables.Add(keyName, true);
}
A very simple solution: add a bit field to the entity called "DisableIndexRecalculation." When your first plugin runs, make sure to set that field to true for all of your updates. In the same plugin, check to see if "DisableIndexRecalculation" is set to true: if so, set it to null (by removing it from the TargetEntity entirely) and stop executing the plugin. If it is null, do your index recalculation.
Because you are immediately removing the field from the TargetEntity if it is true the value will never be persisted to the database so there will be no performance penalty.
A TreeView item and data holds a filename and the data holds a TBitmap. My question pertains to somehow saving the items and the data in the treeview so the items and data can be accessed in a thread. If this is possible, after saving items and data, I could access the items and data in the thread rather than accessing it in Synchronize. As the code written now is too slow because of accessing the GUI TreeItems and Data in Synchronize.
if not Terminated then
begin
Synchronize(
procedure
var
i: integer;
begin
for i := 1 to Form1.TreeView1.Items.Count - 1 do
begin
{ get the bitmap }
iImageEnIO.WIAParams.ProcessingBitmap := iImageEnIO.IEBitmap;
{ The following line prevents me from accessing the TreeView data in a thread }
iImageEnIO.WIAParams.Transfer(TIEWiaItem(Form1.TreeView1.Items[i].Data), False);
{ Set the filename }
iFilename := Form1.TreeView1.Items[i].Text + '.jpg';
{ Add the image to the iIEImageList }
iIndex := iIEImageList.AppendImageRef(TIEBitmap.Create(iImageEnIO.IEBitmap), iFileName);
iIEImageList.Filename[iIndex] := iFileName;
end;
end);
The threads code to access the bitmaps in the thread itself works very well, but if I can move the code that gets the bitmap to the thread rather than in Synchronize would be much better. So my question is "Is there ay way to save a treeview items and data in Synchronize so it can be accessed in the thread outside of Synchronize"?
iImageEnIO.OnProgress := ImageEnProcProgress;
iImageEnIO.OnFinishWork := ImageEnProcFinishWork;
{ Get the bitmap from the imagelist and save the image in the thread }
iCount := iIEImageList.ImageCount;
for j := 0 to iCount-1 do
begin
{ get the filename from the string list }
iFilename := iIEImageList.Filename[j];
{ attach the iIEBitmap2 to iImageEnIO }
iImageEnIO.AttachedIEBitmap := iIEImageList.Image[j];
iPath := IncludeTrailingPathDelimiter(iFolder) + iFilename;
iImageEnIO.SaveToFile(iPath);
end;
I hope I have asked my question correctly and it is clear what I would like to try to do.
I would like to turn this on its head. You want to know how to read the data from the GUI control when executing your thread method. That is a fundamental design flaw. The solution to your problem will involve not attempting to do that at all.
The tree view should not be the owner of the data. It is a GUI control and should merely present a view of the data. The data should be held in a structure that is not bound to the VCL threading rules. Once you have the data structure separated from the GUI, your problem becomes trivial. Once you reach that point, there is no problem to be solved.
So, what kind of a structure do you need? Although it is stored in a tree view, it appears to be a flat list. Store it in a TList<T> container. What do you use for T? Well, that's just the information needed for each item. That could be a record. Or it could be a class. If you are holding non-value type objects in the item type, then a class is probably better. In which case TObjectList<T> is a better fit. So, it would look like this:
type
TItem = class
private
FFileName: string;
FBitmap: TBitmap;
end;
Then your container is simply TObjectList<TItem>. Instantiate it like this:
FItems := TObjectList<TItem>.Create(True);
The True is for the OwnsObjects parameter. This means that when you delete items from the container, or delete the container, the items are destroyed.
At this point you are able to populate the tree view by iterating over the container, and creating nodes to represent the items.
Then when your thread needs to operate on the data, it can refer to FItems which is separated from the GUI control.
The moral of the story is that you should not use your GUI controls as your primary data containers.