winforms - C# OPC Applications Identical Code, but work differently -
i developing c# custom opc client, started off writing in console app quickness, works want to.
then decided make windows form application visual experience.
the windows form application stops working, stops reading data opc server after around minute. console app keeps reading , reading.
i can't find obvious in debug mode either.
i absolutely clutching @ straws here , shed light.
each application using .dll files provided opcfoundation.
here console application
static void main(string[] args) { opc.url url = new opc.url("opcda://localhost/rslinx opc server"); opc.da.server server = null; opccom.factory fact = new opccom.factory(); server = new opc.da.server(fact, null); server.connect(url, new opc.connectdata(new system.net.networkcredential())); // create group opc.da.subscription group; opc.da.subscriptionstate groupstate = new opc.da.subscriptionstate(); groupstate.name = "group"; groupstate.active = true; group = (opc.da.subscription)server.createsubscription(groupstate); // add items group. opc.da.item[] items = new opc.da.item[6]; items[0] = new opc.da.item(); items[0].itemname = "[ux1]f20:9"; items[1] = new opc.da.item(); items[1].itemname = "[ux1]f22:30"; items[2] = new opc.da.item(); items[2].itemname = "[ux1]f22:6"; items[3] = new opc.da.item(); items[3].itemname = "[ux1]f18:8"; items[4] = new opc.da.item(); items[4].itemname = "[ux1]f22:32"; items[5] = new opc.da.item(); items[5].itemname = "[ux1]f22:5"; items = group.additems(items); group.datachanged += new opc.da.datachangedeventhandler(ontransactioncompleted); } static void ontransactioncompleted(object group, object hreq, opc.da.itemvalueresult[] items) { console.writeline("------------------->"); console.writeline("datachanged ..."); (int = 0; < items.getlength(0); i++) { console.writeline("item datachange - itemid: {0}", items[i].itemname); console.writeline(" value: {0,-20}", items[i].value); console.writeline(" timestamp: {0:00}:{1:00}:{2:00}.{3:000}", items[i].timestamp.hour, items[i].timestamp.minute, items[i].timestamp.second, items[i].timestamp.millisecond); } console.writeline("-------------------<"); }
here winform application
public form1() { initializecomponent(); _form1 = this; } public static form1 _form1; public void update(string message) { this.richtextbox1.text = message; } private void form1_load(object sender, eventargs e) { readplc(); } static void readplc() { opc.url url = new opc.url("opcda://localhost/rslinx opc server"); opc.da.server server = null; opccom.factory fact = new opccom.factory(); server = new opc.da.server(fact, null); server.connect(url, new opc.connectdata(new system.net.networkcredential())); // create group opc.da.subscription group; opc.da.subscriptionstate groupstate = new opc.da.subscriptionstate(); groupstate.name = "group"; groupstate.active = true; group = (opc.da.subscription)server.createsubscription(groupstate); // add items group. opc.da.item[] items = new opc.da.item[6]; items[0] = new opc.da.item(); items[0].itemname = "[ux1]f20:9"; items[1] = new opc.da.item(); items[1].itemname = "[ux1]f22:30"; items[2] = new opc.da.item(); items[2].itemname = "[ux1]f22:6"; items[3] = new opc.da.item(); items[3].itemname = "[ux1]f18:8"; items[4] = new opc.da.item(); items[4].itemname = "[ux1]f22:32"; items[5] = new opc.da.item(); items[5].itemname = "[ux1]f22:5"; items = group.additems(items); group.datachanged += new opc.da.datachangedeventhandler(ontransactioncompleted); } static void ontransactioncompleted(object group, object hreq, opc.da.itemvalueresult[] items) { (int = 0; < items.getlength(0); i++) { uiupdater text = new uiupdater(); text.updatetext(items.getlength(0).tostring() + " t " + i.tostring() + "item datachange - itemid:" + items[i].itemname + "value: " + items[i].value + " timestamp: " + items[i].timestamp.hour + ":" + items[i].timestamp.minute + ":" + items[i].timestamp.second + ":" + items[i].timestamp.millisecond); } }
uiupdate class
class uiupdater { public void updatetext(string data) { form1._form1.update(data); } public class updateui { public int updatedrows { get; set; } public string custom1 { get; set; } public string custom2 { get; set; } public string custom3 { get; set; } public string exception { get; set; } public plctextstatus plcstatus { get; set; } }
any questions please ask!
as suspected, cross-threading issue. problem can't update ui other thread ui thread. event transaction completed gets called on separate thread, updating ui.
it works little while because relatively tolerant of errors, reaching point deadlocking or throwing exception isn't getting caught (or reported).
the fix simple enough though.
in method:
public void update(string message) { this.richtextbox1.text = message; }
change to:
public void update(string message) { richtextbox1.invoke( (methodinvoker) delegate { richtextbox1.text = message; }); }
what tells richtextbox1
"invoke" or run following delegate (function) on owning thread (aka, ui thread).
you should try avoid using static
methods , references in code. don't see reason code have shouldn't instance methods instead of static ones.
just side note, write opc programs deal thousands of tags , hundreds of ui updates per second. doing works small demo programs won't scale well. when architecture grows, need start batching ui updates aren't busy calling ui thread repeatedly inside of update.
edit
another problem have using local references (to opc server, , subscription example) , demonstrates memory leak , zombie object. happening readplc
method goes out of scope , you've created references objects inside being held in memory. since have no way unsubscribe event, event keeps firing. these variables should declared outside scope of readplc
method can unsubscribe event , shutdown opc server. otherwise leaving zombie subscriptions (look @ rslinx opc diagnostics page, you'll see subscriptions sitting there).
Comments
Post a Comment