Class member behave differently when define as static or non static - wpf

I have WPF application with PcapDotNet DLL's that measure my machine Interface Rate.
This is the Model:
public class Interface
{
public PacketDevice PacketDevice { get { return livePacketDevice; } }
private DateTime _lastTimestamp;
private double _bitsPerSecond;
private double _packetsPerSecond;
private DateTime _lastTimestamp;
private static List<Interface> _machineInterfaces; // list of all machine interfaces
public void Start(Interface inf)
{
OpenAdapterForStatistics(inf.PacketDevice);
}
public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
if (selectedOutputDevice != null)
{
using (PacketCommunicator statCommunicator = selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000)) //open the output adapter
{
try
{
statCommunicator.Mode = PacketCommunicatorMode.Statistics; //put the interface in statstics mode
statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
}
catch (Exception)
{ }
}
}
}
private void StatisticsHandler(PacketSampleStatistics statistics)
{
DateTime currentTimestamp = statistics.Timestamp; //current sample time
DateTime previousTimestamp = _lastTimestamp; //previous sample time
_lastTimestamp = currentTimestamp; //set _lastTimestamp for the next iteration
if (previousTimestamp == DateTime.MinValue) //if there wasn't a previous sample than skip this iteration (it's the first iteration)
return;
double delayInSeconds = (currentTimestamp - previousTimestamp).TotalSeconds; //calculate the delay from the last sample
_bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
_packetsPerSecond = statistics.AcceptedPackets / delayInSeconds; //calculate packets per second
if (NewPointEventHandler != null)
NewPointEventHandler(_bitsPerSecond);
double value = _packetsPerSecond;
}
As you can see Start method start to measure the Interface rate and put the values into 2 fields:
_bitsPerSecond and _packetsPerSecond.
So after the application start i have this field:
List<Interface> _machineInterfaces;
That read all my machine interfaces.
After that i start my Start method:
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
}
OK so now this is my Question:
This is my Timer Tick Event:
public RadObservableCollection<double> mbitPerSecondValue { get; private set; }
If my BitsPerSecond Class Interface member define as regular and not Static it's value is always zero:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
mbitPerSecondValue.Add(bps);
}
In case BitsPerSecond define as static all good:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
double bps = Interface.BitsPerSecond;
mbitPerSecondValue.Add(bps);
}
So my question is why ?
Edit
Currently i changed my function:
private void StartStatistics()
{
int index = lvAdapters.SelectedIndex;
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
foreach (Interface item in Interface.MachineInterfaces)
item.Start();
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start();
}
What i want to achieve is to open statistics on each interface on my machine , but again in the first interface (i have 2) i can see the traffic changing (BitsPerSecond) but in the second interface it's always zero (i make sure to generate some traffic through this interface so it not supposed to be zero)

For your second question, try to call each Interface's Start from different threads. The only suspicious thing I see is that maybe statCommunicator.ReceiveStatistics is blocking the thread and preventing the other Interfaces from being Started.
This should avoid that problem:
private void StartStatistics()
{
foreach (Interface item in Interface.MachineInterfaces)
{
ThreadStart tStarter = delegate
{
item.Start();
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
}
statisticsTimer.Start();
}

Well, it's obvious why it's working when defined as static: all instances of Interface are sharing the same property, so when you increment its value from one place, the new value is automatically available everywhere.
But as a regular non-static property, you have to make sure you're reading from the same instance you've previously modified. And you're not.
First of all, you're creating a new Interface (let's call it Interface A) and then calling its Start, passing another Interface (that we'll call Interface B) that you get from Interface.MachineInterfaces, as parameter:
private void StartStatistics()
{
...
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
...
}
Inside the Start method of Interface A, you're subscribing to the statistics of Interface B, but the handler is still in Interface A:
public void Start(Interface inf)
{
OpenAdapterForStatistics(inf.PacketDevice);
}
public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
...
statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
...
}
And when the handler in Interface A is called, it increments its own _bitsPerSecond value. Not Interface B's, but Interface A's.
private void StatisticsHandler(PacketSampleStatistics statistics)
{
...
_bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
...
}
But in the end, you're checking the value of BitsPerSecond in Interface B, taken again from Interface.MachineInterfaces!
private void statisticsTimer_Tick(object sender, EventArgs e)
{
...
double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
...
}
-- SUGGESTED SOLUTION 1 --
Why don't you just make it so Start uses its own instance, so you don't have to create a new Interface just to use it?
public void Start()
{
OpenAdapterForStatistics(this.PacketDevice);
}
That way you can just do:
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
ThreadStart tStarter = delegate
{
Interface.MachineInterfaces[index].Start(); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
}
...And you should get the desired output in your Timer Tick callback.
-- SUGGESTED SOLUTION 2 --
If you don't want to call Start from the original Interfaces inside Interface.MachineInterfaces, then you'll have to store the new Interface in some kind of Dictionary, so you can access it later to get BitsPerSecond from it:
private Dictionary<Interface, Interface> InterfaceDictionary = new Dictionary<Interface, Interface>();
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
if (InterfaceDictionary.ContainsKey(Interface.MachineInterfaces[index]))
InterfaceDictionary[Interface.MachineInterfaces[index]] = inf;
else
InterfaceDictionary.Add(Interface.MachineInterfaces[index], inf);
}
And in your Timer Tick callback, grab the data from the associated Interface, not from the one in Interface.MachineInterfaces:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
var interface = InterfaceDictionary[Interface.MachineInterfaces[index]];
double bps = interface.BitsPerSecond;
mbitPerSecondValue.Add(bps);
}

Related

ArgOutOfRangeEx while calling ListViews base.WndProc when click occurs outside subitems, C# 10.0

I am receiving a ArgumentOutOfRangeException while calling base.WndProc in an OwnerDrawn listview.
The exception occurs after a click is performed to the right (empty space) of the last subitem of any ListViewItem.
The listview column widths are programmatically set to fill the entire listview, however on occasion data is not received as expected leading to some extra space, this is an edge case which usually does not occur.
The message to be processed at the time of exception is WM_LBUTTONDOWN (0x0201), or WM_RBUTTONDOWN (0x0204).
All data for painting the LV subitems is from a class referenced by the tag of the LVI, at no point do I try to read the subitems, nor do I write data to any of the subitems.
Below is the smallest reproducible code, in C# (10.0), using .NET 6.0 that shows the exception.
I attempted to simply exclude processing of WM_LBUTTONDOWN. This did remove the exception, however it also stopped left click events from reaching MouseUp which is required. (also for right clicks)
Whilst I am working to fix my errors in column sizing after receiving bad or no data, I would like to check for this possible exception and simply return from the method before the exception can occur.
Data class
namespace testCase;
class myClass
{
public string s1 = "subitem1";
public string s2 = "subitem2";
}
Form code
namespace testCase;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ListViewExWatch lv = new();
Controls.Add(lv);
lv.MouseUp += Lv_MouseUp;
lv.Top = 0;
lv.Left = 0;
lv.Width = ClientSize.Width;
lv.Height = ClientSize.Height;
lv.OwnerDraw = true;
lv.BackColor = Color.AntiqueWhite;
lv.Columns.Add("Row", 50);
lv.Columns.Add("sub1", 50);
lv.Columns.Add("sub2", 50);
for(int i = 0; i < 10; i++)
{
ListViewItem lvi = new(){ Text = "Row " + i, Tag = new myClass() };
lvi.SubItems.Add("");
lvi.SubItems.Add("");
lv.Items.Add(lvi);
}
}
private void Lv_MouseUp(object? sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
MessageBox.Show("Left click - doing action A");
}
if (e.Button == MouseButtons.Right)
{
MessageBox.Show("Right click - Creating context menu");
}
}
}
Custom ListView override
using System.Runtime.InteropServices;
namespace testCase;
public class ListViewExWatch : ListView
{
#region Windows API
[StructLayout(LayoutKind.Sequential)]
struct DRAWITEMSTRUCT
{
public int ctlType;
public int ctlID;
public int itemID;
public int itemAction;
public int itemState;
public IntPtr hWndItem;
public IntPtr hDC;
public int rcLeft;
public int rcTop;
public int rcRight;
public int rcBottom;
public IntPtr itemData;
}
const int LVS_OWNERDRAWFIXED = 0x0400;
const int WM_SHOWWINDOW = 0x0018;
const int WM_DRAWITEM = 0x002B;
const int WM_MEASUREITEM = 0x002C;
const int WM_REFLECT = 0x2000;
const int WM_LBUTTONDOWN = 0x0201;
#endregion
public ListViewExWatch()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
protected override CreateParams CreateParams
{
get
{
CreateParams k_Params = base.CreateParams;
k_Params.Style |= LVS_OWNERDRAWFIXED;
return k_Params;
}
}
protected override void WndProc(ref Message k_Msg)
{
//if (k_Msg.Msg == WM_LBUTTONDOWN) return;
base.WndProc(ref k_Msg); // Exception: System.ArgumentOutOfRangeException: 'InvalidArgument=Value of '-1' is not valid for 'index'.
// Only occurs when clicking to the right of the last subItem
switch (k_Msg.Msg)
{
case WM_SHOWWINDOW:
View = View.Details;
OwnerDraw = false;
break;
case WM_REFLECT + WM_MEASUREITEM:
Marshal.WriteInt32(k_Msg.LParam + 4 * sizeof(int), 14);
k_Msg.Result = (IntPtr)1;
break;
case WM_REFLECT + WM_DRAWITEM:
{
object? lParam = k_Msg.GetLParam(typeof(DRAWITEMSTRUCT));
if (lParam is null) throw new Exception("lParam shouldn't be null");
DRAWITEMSTRUCT k_Draw = (DRAWITEMSTRUCT)lParam;
using Graphics gfx = Graphics.FromHdc(k_Draw.hDC);
ListViewItem lvi = Items[k_Draw.itemID];
myClass wi = (myClass)lvi.Tag;
TextRenderer.DrawText(gfx, lvi.Text, Font, lvi.SubItems[0].Bounds, Color.Black, TextFormatFlags.Left);
TextRenderer.DrawText(gfx, wi.s1, Font, lvi.SubItems[1].Bounds, Color.Black, TextFormatFlags.Left);
TextRenderer.DrawText(gfx, wi.s2, Font, lvi.SubItems[2].Bounds, Color.Black, TextFormatFlags.Left);
break;
}
}
}
}
The error message
System.ArgumentOutOfRangeException
HResult=0x80131502
Message=InvalidArgument=Value of '-1' is not valid for 'index'. Arg_ParamName_Name
ArgumentOutOfRange_ActualValue
Source=System.Windows.Forms
StackTrace:
at System.Windows.Forms.ListViewItem.ListViewSubItemCollection.get_Item(Int32 index)
at System.Windows.Forms.ListView.HitTest(Int32 x, Int32 y)
at System.Windows.Forms.ListView.ListViewAccessibleObject.HitTest(Int32 x, Int32 y)
at System.Windows.Forms.ListView.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.ListView.WndProc(Message& m)
at testCase.ListViewExWatch.WndProc(Message& k_Msg) in C:\Users\XXXX\source\repos\testCase\ListViewExWatch.cs:line 50
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
From the error it is apparent that the base listview code it is trying to get a subitem that doesn't exist because the click is not on a subitem, hence the -1 for index within the error message.
At the time of exception there is no member of k_Msg that contains the index (-1), so I cannot check for that and simply return.
I could surround the call to base.WndProc in a try catch, since it is an edge case. I've always had the mindset to check for possible exceptions and prevent them whenever possible rather than catch them. But this one has me stumped.
Am I being too pedantic in this regard?
Most likely I am missing something basic here, could you fine folks please point me in the right direction?
I had implemented the following code to catch only this specific exception and still allow any other to bubble up as desired.
try
{
base.WndProc(ref k_Msg); // This throws a ArgOutOfRangeEx when a click is performed to the right of any subitem (empty space)
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "index" && (int?)ex.ActualValue == -1 && ex.TargetSite?.DeclaringType?.Name == "ListViewSubItemCollection")
{
Program.Log(LogLevel.Normal, "ListViewExWatch.WndProc()", "ArgumentOutOfRangeException: A click has been perfored outside of a valid subitem. This has been handled and indicates column witdth calcs were wrong.");
return;
}
With further research however, I managed to specifically workaround the issue
From https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttondown
lParam
The low-order word specifies the x-coordinate of the cursor...
The high-order word specifies the y-coordinate of the cursor...
I ended up modifying the lParam before processing the message. By moving the x-coordinate to inside the last subitem the exception is averted and clicks are processed as normal.
protected override void WndProc(ref Message k_Msg)
{
if (k_Msg.Msg == WM_LBUTTONDOWN || k_Msg.Msg == WM_RBUTTONDOWN)
{
// Get the x position of the end of the last subitem
int width = -1;
foreach (ColumnHeader col in Columns) width += col.Width;
// Where did the click occur?
int x_click = SignedLOWord(k_Msg.LParam);
int y_click = SignedHIWord(k_Msg.LParam);
// If the click is to the right of the last subitem, set the x-coordinate to inside the last subitem.
if (x_click > width) k_Msg.LParam = MakeLparam(width, y_click);
}
base.WndProc(ref k_Msg);
...
It is interesting to note the correct x and y coordinates are still reported in the Lv_MouseUp handler. This may not be the case for other handlers such as MouseDown which has obviously been modified, since I do not use them it has not presented a problem
Here are the helper functions used above (from referencesource.microsoft.com)
public static IntPtr MakeLparam(int low, int high)
{
return (IntPtr)((high << 16) | (low & 0xffff));
}
public static int SignedHIWord(IntPtr n)
{
return SignedHIWord(unchecked((int)(long)n));
}
public static int SignedLOWord(IntPtr n)
{
return SignedLOWord(unchecked((int)(long)n));
}
public static int SignedHIWord(int n)
{
return (short)((n >> 16) & 0xffff);
}
public static int SignedLOWord(int n)
{
return (short)(n & 0xFFFF);
}

print() stream not showing up in flink UI task manager STD OUT [duplicate]

In Apache Flink, I am not able to see the output in std out, but my job is running successfully and data is coming
As you are running your job on a cluster, DataStreams are printed to the stdout of the TaskManager process. This TaskManager stdout is directed to an .out file in the ./log/ directory of the Flink root directory. I believe this is here you have seen your output.
I don't know if it is possible to change the stdout of TaskManagers, however, a quick and dirty solution could be to write the output to a socket :
output.writeToSocket(outputHost, outputPort, new SimpleStringSchema())
public static void main(String[] args) throws Exception {
// the host and the port to connect to
final String hostname = "192.168.1.73";
final int port = 9000;
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createRemoteEnvironment("192.168.1.68", 6123);
// get input data by connecting to the socket
DataStream<String> text = env.socketTextStream(hostname, port, "\n");
// parse the data, group it, window it, and aggregate the counts
DataStream<WordWithCount> windowCounts = text
.flatMap(new FlatMapFunction<String, WordWithCount>() {
public void flatMap(String value, Collector<WordWithCount> out) {
for (String word : value.split("\\s")) {
out.collect(new WordWithCount(word, 1L));
}
}
})
.keyBy("word").timeWindow(Time.seconds(5))
.reduce(new ReduceFunction<WordWithCount>() {
public WordWithCount reduce(WordWithCount a, WordWithCount b) {
return new WordWithCount(a.word, a.count + b.count);
}
});
// print the results with a single thread, rather than in parallel
windowCounts.print().setParallelism(1);
env.execute("Socket Window WordCount");
}
public static class WordWithCount {
public String word;
public long count;
public WordWithCount() {
}
public WordWithCount(String word, long count) {
this.word = word;
this.count = count;
}
#Override
public String toString() {
return word + " : " + count;
}
}

How do I limit the concurrent number of readers in a first readers writers problem?

I would like to understand how implement some form of concurrency that would limit the number of readers.
void *reader(void *rno)
{
// Reader acquire the lock before modifying numreader
pthread_mutex_lock(&mutex);
numreader++;
if(numreader == 1) {
sem_wait(&wrt);
}
pthread_mutex_unlock(&mutex);
// Reading Section
printf("Reader %d: read cnt as %d\n",*((int *)rno),cnt);
// Reader acquire the lock before modifying numreader
pthread_mutex_lock(&mutex);
numreader--;
if(numreader == 0) {
sem_post(&wrt);
}
pthread_mutex_unlock(&mutex);
}
I've tried this, While this just enables all readers to run and calls the writers afterwards, I want to understand how to call a limited number of readers at a time, then, allow writers to run.
wrt is a binary semaphore but if it is a counting semaphore initialized to the limit, does this help in achieving the goal?
Yes, a counting semaphore with the count set to the max reader count can solve it. Below is a little Java code you can run to understand.
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.currentTimeMillis;
class CountingSemaphore {
private final int maxCount;
private int signals = 0;
public CountingSemaphore(int maxCount) {
this.maxCount = maxCount;
}
public synchronized void take() throws InterruptedException {
while (signals==this.maxCount) wait();
this.signals++;
this.notify();
}
public synchronized void release() throws InterruptedException{
while(this.signals == 0) wait();
this.signals--;
this.notify();
}
public synchronized int getThreadCount(){
return signals;
}
}
class Reader{
private final int id;
public Reader(int id) {
this.id = id;
}
public void read(CountingSemaphore semaphore) throws InterruptedException {
semaphore.take();
System.out.printf("Reader %d is reading. Reader count is %d%n", id, semaphore.getThreadCount());
semaphore.release();
}
}
public class ConcurrentReaderTest {
public static void main(String[] args){
CountingSemaphore countingSemaphore = new CountingSemaphore(5);
List<Thread> readerThreads = new ArrayList<>();
for(int i=0;i<10;i++){
Reader reader = new Reader(i);
readerThreads.add(new Thread(()-> {
try {
reader.read(countingSemaphore);
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
}
readerThreads.stream().forEach(r->r.start());
long startTime = currentTimeMillis();
readerThreads.stream().forEach(r->r.run());
System.out.println("time taken: "+ String.valueOf(currentTimeMillis()-startTime));
}
}

GMap.NET WPF Marker timer update

I have very little experience using threads so I might be missing some pretty basic infomration in my logic.
Anyway, I'm trying to update a GMapMarker based on 1000ms timer.
My Timer start:
aTimer = new System.Timers.Timer();
aTimer.Elapsed += OnTimerEvent;
aTimer.Interval = 1000;
aTimer.Enabled = true;
My update code:
int _positionIncriment = 0;
private void OnTimerEvent(object sender, System.Timers.ElapsedEventArgs e)
{
PointLatLng p = m_debugPath[_positionIncriment];
_gpsMarker.Position = p;
_positionIncriment++;
_gmap.ForceUpdateOverlays();
if(_positionIncriment >= m_debugPath.Count)
{
_positionIncriment = 0;
}
}
As I step through this block, it stops at the ForceUpdateOverlays and never progresses further.
As far as I can tell the render functions of GMap is within its own thread which might be causing issue.. but as I said, only have a basic understanding of threading I'm a bit lost.
Any help would be great.
Use a DispatcherTimer instead of Timer. Its Tick event is fired in the UI thread:
using System.Windows.Threading;
...
aTimer = new DispatcherTimer();
aTimer.Tick += OnTimerEvent;
aTimer.Interval = TimeSpan.FromSeconds(1);
aTimer.IsEnabled = true; // or aTimer.Start();
...
private void OnTimerEvent(object sender, EventArgs e)
{
...
}
Looks like I needed to invoke the Dispatcher before running my code.
I don't know is this the the 'correct' way to solve this problem, but the end result is what i was desiring.
Hope this helps someone else.
int _positionIncrement = 0;
private void OnTimerEvent(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
PointLatLng p = m_debugPath[_positionIncrement];
_gps.Position = p;
_positionIncrement++;
_gmap.ForceUpdateOverlays();
if(_positionIncrement >= m_debugPath.Count)
{
_positionIncrement = 0;
}
});
}

"Undefined for type" and "cannot make static reference"?

I'm writing a program but I'm unable to call a few of the methods I made. The errors are as follows:
-method reportMenu(String) in the type CommissionReportSystem is not applicable for the arguments ()
-Cannot make a static reference to the non-static method getSalesData() from the type CommissionReportSystem
-The method computeTotalSales() is undefined for the type CommissionReportSystem
-The method computeSalesCommission(double) in the type CommissionReportSystem is not applicable for the arguments ()
-The method showAgentCommission(double) in the type CommissionReportSystem is not applicable for the arguments ()
I've tried a lot of fixes but nothing seems to be sticking and I'm unsure of how to proceed. I've included the relevant parts of the code below. I would appreciate any tips on how to fix any of these. Thank you!
import java.io.*;
import java.text.*;
import java.util.*;
public class CommissionReportSystem {
private static final String String = null;
public static void main(String[] args) {
getSalesData ();
computeTotalSales ();
computeSalesCommission ();
showAgentCommission ();
shutdown ();
}
String [] getSalesData (){
String [] data = new String [2];
String ticketsSold = "";
String ticketPrice = "";
String buffer = new String ();
data[0] = buffer;
data[1] = buffer;
BufferedReader br = null;
try {
br = new BufferedReader (new InputStreamReader(System.in));
System.out.print ("Enter tickets sold:");
buffer = br.readLine ();
ticketsSold = buffer;
System.out.print ("Enter ticket price:");
buffer = br.readLine ();
ticketPrice = buffer;
} catch (Exception e) {
System.out.println ("Invalid entry");
}
data [0] = ticketsSold;
data [1] = ticketPrice;
return data;
}
public static double totalSales (String ticketsSold, String ticketPrice){
int ticketsSoldNum = Integer.parseInt(ticketsSold);
double ticketPriceNum = Double.parseDouble(ticketPrice);
double totalSalesNum = ticketsSoldNum * ticketPriceNum;
return totalSalesNum;}
public static final double computeSalesCommission (double totalSalesNum){
final double rate1 = 0.025;
final double rate2 = 0.0375;
final double rate3 = 0.0425;
final double salesLimit1 = 2000;
final double salesLimit2 = 4000;
final double agentCommission= 0;
if (totalSalesNum <= 2000) {
agentCommission = rate1 * totalSalesNum;
}
else if (totalSalesNum <= 4000){
agentCommission = rate2 * totalSalesNum;
}
else (totalSalesNum > 4000){
agentCommission = rate3 * totalSalesNum;
}
return agentCommission;
}
public static void showAgentCommission (double agentCommission){
System.out.format ("Congratulation agent Cindy Smith, your current daily commission:" + agentCommission);
}
public static void shutdown (){
System.out.format ("Thank you for your time! Have a great day!");
}
public static void handleInvalidData (){
}
}
1) getSalesData() is an instance method. If you want to call an instance method, create an object of the class and call method using that. Else you have to make the method static. Remember one the thing you cannot access the instance variables inside static method.
2) There is no method computeTotalSales() in your class.
3) computeSalesCommission() requires an argument of type double. You have called it without any argument.
4) The last comment is also valid for showAgentCommission().

Resources