I want to schedule tasks with the Microsoft Solver Framework. For now i have the simple goal to just order the tasks in a queue so that i get a minimal project time. (later i want to have more than one queue). I tried to approach this with the following setup:
Decision:
projectFinish
start
finish
Parameter:
duration
Constraint:
start + duration = finish
not more than one task at a time
projectFinish after all tasks finished
Goal:
minimize projectFinish
Here is my code so far
static void Main(string[] args) {
var data = new List<Task>() {
new Task(){ Duration = 1, Name = "task0"},
new Task(){ Duration = 1, Name = "task1"},
new Task(){ Duration = 1, Name = "task2"},
};
SolveScheduling(data);
}
public class Task {
private static int id_counter = 0;
public Task() { ID = id_counter++; }
public int ID { get; private set; }
public string Name { get; set; }
public double Duration { get; set; }
}
private static void SolveScheduling(IEnumerable<Task> data) {
SolverContext context = SolverContext.GetContext();
Model model = context.CreateModel();
var set = new Set(Domain.Any,"TaskSet");
var projectFinish = new Decision(Domain.IntegerNonnegative, "projectFinish");
model.AddDecision(projectFinish);
var taskSet = new Set(Domain.Any, "tasks");
var durations = new Parameter(Domain.RealNonnegative, "durations", taskSet);
durations.SetBinding(data, "Duration", "Name");
var ids = new Parameter(Domain.Integer, "ids", taskSet);
ids.SetBinding(data, "ID", "Name");
var starts = new Decision(Domain.RealNonnegative, "starts", taskSet);
var finishs = new Decision(Domain.RealNonnegative, "finishs", taskSet);
model.AddDecisions(starts, finishs);
model.AddParameters(durations, ids);
// Constraints
// start + duration = finish
model.AddConstraint("constraint0", Model.ForEach(taskSet, (t) => starts[t] + durations[t] == finishs[t]));
// Tasks after each other
model.AddConstraint("constraint1", Model.ForEach(taskSet, t =>
Model.ForEachWhere(taskSet, t2 => Model.Or(finishs[t] < starts[t2] , starts[t] > finishs[t2]), (t2) => ids[t] != ids[t2])));
// projectFinish after all tasks finished
model.AddConstraint("constraint2", Model.ForEach(taskSet, t => projectFinish >= finishs[t]));
// Goals
model.AddGoal("goal0", GoalKind.Minimize, projectFinish);
Solution solution = context.Solve();//new SimplexDirective());
Report report = solution.GetReport();
Console.WriteLine(#"===== report =====");
Console.Write("{0}", report);
Console.ReadLine();
}
Now the problem is that it takes for ever to solve this (although it are only 3 tasks and 1 queue). What am i missing here and how can i improve the speed of solving.
Update
I found a solution for my problem. If you have any improvements feel free to comment. Here is my code:
SolverContext context = SolverContext.GetContext();
Model model = context.CreateModel();
// === Sets ===
var taskSet = new Set(0,data.Count(), 1);
// === Parameters ===
var duration = new Parameter(Domain.RealNonnegative, "durations", taskSet);
var id = new Parameter(Domain.RealNonnegative, "id", taskSet);
duration.SetBinding(data, "Duration", "ID");
id.SetBinding(data, "ID", "ID");
model.AddParameters(duration, id);
// === Decisions ===
var projectFinish = new Decision(Domain.RealNonnegative, "projectFinish");
var start = new Decision(Domain.RealNonnegative, "starts", taskSet);
var finish = new Decision(Domain.RealNonnegative, "finishs", taskSet);
model.AddDecisions(projectFinish, start, finish);
// === Constraints ===
model.AddConstraint("constraint0", start[0] == 0);
// start + duration = finish
model.AddConstraint("constraint1", Model.ForEach(taskSet, (t) => start[t] + duration[t] == finish[t]));
// projectFinish after all tasks finished
model.AddConstraint("constraint2", Model.ForEach(taskSet, t => projectFinish >= finish[t]));
// not more than one task at a time
model.AddConstraint("constraint3", Model.ForEach(taskSet, t =>
Model.ForEachWhere(taskSet, t2 => Model.Or(finish[t] < start[t2], start[t] > finish[t2]), (t2) => id[t] != id[t2])));
// === Goals ===
model.AddGoal("goal0", GoalKind.Minimize, projectFinish); // minimieren der projekt zeit
// === Solve ===
context.CheckModel();
Solution solution = context.Solve();
I found a solution that works for me. I changed the taskSet
var taskSet = new Set(0, data.Count(), 1);
and added a new constraint
model.AddConstraint("constraint", starts[0] == 0);
I updated the question
Related
In my ASP.NET Core 6 Web API, I have used the code below to calculate Total Annual Sales (from Transactions):
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
string transactionMonth = DateTime.Now.ToString("MM");
decimal totalMonthlyTransactions = 0;
var sales = await _dbContext.Sales.ToListAsync();
foreach (var item in sales)
{
var salesDate = item.CreatedAt.ToString();
var salesMonth = salesDate.Substring(3, 2);
if (transactionMonth == salesMonth)
{
totalMonthlyTransactions += item.Amount;
}
}
return totalMonthlyTransactions;
}
How to I re-write the code above to get TotalAnnualSales (Transactions)?
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
int currentYear = DateTime.Now.Year;
return (await dbContext.Sales.Where(t=>t.CreatedAt.Year==currentYear).ToListAsync()).Sum(item=>item.Amount);
}
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
decimal totalAnnualTransactions = 0;
int _year = DateTime.Now.Year;
var sales = await dbContext.Sales.Where(y=>y.CreatedAt.Year==_year).ToListAsync();
// The first method
foreach (var item in sales)
{
totalAnnualTransactions += item.Amount;
}
// The second method
//double totalAnnualTransactions= sales.Sum(item => item.Amount);
return totalAnnualTransactions;
}
Instead of converting the datetimes to strings, try accessing them as ints. You can do this with .Month or .Year respectively
You can try to calculate from and to date range based on a specified month, then query/filter sales data with these from and to date range, like below.
var date1 = DateTime.Now;
var days = DateTime.DaysInMonth(date1.Year, date1.Month);
var from = new DateTime(date1.Year, date1.Month, 1, 0, 0, 0);
var to = new DateTime(date1.Year, date1.Month, days, 23, 59, 59);
var totalMonthlyTransactions = sales.AsEnumerable().Where(s => s.CreatedAt >= from && s.CreatedAt <= to).Select(s => s.Amount).Sum();
I hope following code will work for you...
private async Task<IEnumerable<object>> GetMonthlyTransactions(int year)
{
var starting = new DateTime(year, 1, 1).Date;
var sales = await context.Sales.Where(f => f.SalesDate >= starting).Select(s => new
{
Date = s.SalesDate.ToString("MM-yyyy"),
Amount = s.NetAmount
}).ToListAsync();
var finaldata = sales.GroupBy(s => s.Date, (d, a) => new { Date = d, Am = a }).Select(l => new
{
Date = l.Date,
Amount = l.Am.Sum(h => h.Amount)
});
return finaldata;
}
So I have a UWP project that I handle bookings on a group of rooms in.
I get all of my values from a Json API.
I want to make like a loop that checks if a room is booked or not every minute or something like that, but I have no idé how to do it.
This is how I get all the rooms with the bookings and all the attributes to them:
public async void addroom()
{
string url = "https://api.booking.com/api/company/07ce8f7c-f3d3-4df2-84bd-33f8fc263deb/rooms";
HttpClient client = new HttpClient();
string response = await client.GetStringAsync(url);
List<Class2> data = JsonConvert.DeserializeObject<List<Class2>>(response);
foreach (Class2 room in data)
{
string booking = $"https://api.booking.com/api/company/07ce8f7c-f3d3-4df2-84bd-33f8fc263deb/rooms/{room.id}/bookings";
HttpClient BookingClient = new HttpClient();
string BookingResponse = await BookingClient.GetStringAsync(booking);
List<Bookings> bookings = JsonConvert.DeserializeObject<List<Bookings>>(BookingResponse);
room.Bookings = bookings;
string id = room.id;
string name = room.name;
int seats = room.seats;
Uri Img = room.ImageUrl;
List<Roomattribute> roomattrib = room.roomAttributes;
var NewRoom = new Room
{
RoomID = id,
RoomName = name,
FrontImage = Img,
Seats = seats,
};
foreach (var books in bookings)
{
string note = books.note;
DateTime TimeFrom = books.timeFrom;
DateTime TimeTo = books.timeTo;
Class2 BookRoom = books.room;
string BookId = books.id;
DateTime Now = new DateTime(2018, 04, 25, 09, 40, 00);
var BeforeEnd = books.timeTo.Subtract(Now).Subtract(TimeSpan.FromMinutes(15));
var BeforeBegin = books.timeFrom.Subtract(Now).Subtract(TimeSpan.FromMinutes(15));
if (books.timeFrom <= Now && books.timeTo > Now)
{
ToRed();
DispatcherTimer ColorTimer = new DispatcherTimer();
ColorTimer.Interval = BeforeEnd;
ColorTimer.Tick += (sender, args) =>
{
ToYellow();
ColorTimer.Stop();
};
ColorTimer.Start();
}
else if (books.timeTo == Now)
{
ToGreen();
}
else
{
DispatcherTimer ColorTimer = new DispatcherTimer();
ColorTimer.Interval = BeforeBegin;
ColorTimer.Tick += (sender, args) =>
{
ToYellow();
ColorTimer.Stop();
};
ColorTimer.Start();
}
}
foreach (var attri in roomattrib)
{
int attriId = attri.id;
string attriName = attri.name;
int attriIcon = attri.icon;
if (room.roomAttributes.Any(a => a.id == 1))
{
NewRoom.Tv = Visibility.Visible;
}
else if (room.roomAttributes.Any(a => a.id != 1))
{
NewRoom.Tv = Visibility.Collapsed;
}
if (room.roomAttributes.Any(a => a.id == 2))
{
NewRoom.Wifi = Visibility.Visible;
}
else if (room.roomAttributes.Any(a => a.id != 2))
{
NewRoom.Wifi = Visibility.Collapsed;
}
if (room.roomAttributes.Any(a => a.id == 3))
{
NewRoom.Projector = Visibility.Visible;
}
else if (room.roomAttributes.Any(a => a.id != 3))
{
NewRoom.Projector = Visibility.Collapsed;
}
if (room.roomAttributes.Any(a => a.id == 4))
{
NewRoom.Wboard = Visibility.Visible;
}
else if (room.roomAttributes.Any(a => a.id != 4))
{
NewRoom.Wboard = Visibility.Collapsed;
}
}
Rooms.Add(NewRoom);
}
}
Right now all of my code is working perfectly (Apart from that the all bookings goes to all of the rooms but that is off topic...) and when a room is unoccupied it has a green LinearGredientBrush and when a room gets booked it is changing color to red and when it is 15 min until the room is unoccupied the color is changing to yellow.
What I need the check for is for example if a room is canceled before the time runs out.
I was thinking that put all of this in a For loop could be a solution:
var BeforeEnd = books.timeTo.Subtract(Now).Subtract(TimeSpan.FromMinutes(15));
var BeforeBegin = books.timeFrom.Subtract(Now).Subtract(TimeSpan.FromMinutes(15));
if (books.timeFrom <= Now && books.timeTo > Now)
{
ToRed();
DispatcherTimer ColorTimer = new DispatcherTimer();
ColorTimer.Interval = BeforeEnd;
ColorTimer.Tick += (sender, args) =>
{
ToYellow();
ColorTimer.Stop();
};
ColorTimer.Start();
}
else if (books.timeTo == Now)
{
ToGreen();
}
else
{
DispatcherTimer ColorTimer = new DispatcherTimer();
ColorTimer.Interval = BeforeBegin;
ColorTimer.Tick += (sender, args) =>
{
ToYellow();
ColorTimer.Stop();
};
ColorTimer.Start();
}
I hope I described the question well enough, and would be very pleased to get some help with my question.
Thanks in advance!
Instead of creating a DispatcherTimer for each booking separately, you could just create a single one that is triggered once per a time interval, for example once per minute (depending on how often you want to see the color changes).
In the Tick handler, you could then just check for each room:
A booking is active: Red - this check can be done by a simple foreach loop over the bookings on the given room
A booking is starting in less than 15 minutes: Yellow - again a simple foreach loop, check the start times of all bookings, if one is less than 15 minutes away, we have a match
Otherwise: Green
The DispatcherTimer.Tick event fires after the time specified in Interval has elapsed. Tick continues firing at the same Interval until the Stop method is called, the app terminates, or the app is suspended (fires Suspending). So you can put the loop here and specify the internal is one minute.
Besides, if you want your app can run while minimized or under the lock screen, you can use extended execution to achieve it. See the topic Postpone app suspension with extended execution.
---Update---
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
//Get current managed thread ID
Debug.WriteLine(Environment.CurrentManagedThreadId);
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMinutes(1);
timer.Tick += async (ob, arg) =>
{
Debug.WriteLine(Environment.CurrentManagedThreadId);
//You can update the booking room color here
//TODO get data and update room color
//You can also update the booking room color using Dispatcher.RunAsync method.
//This is alternative to update the data on the Tick event above TODO part directly .
//await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
//{
// Debug.WriteLine(Environment.CurrentManagedThreadId);
// //TODO get data and update room color
//});
};
timer.Start();
}
You can see the CoreDispatcher.RunAsync(CoreDispatcherPriority, DispatchedHandler) Method.
I have following code :
void Main()
{
Order order = new Order
{
Catalogen = new List<Catalog>
{
new Catalog
{
Artikels = new List<Artikel>
{
new Artikel{PosNr=1}, new Artikel{PosNr=2}, new Artikel{PosNr=3}
}
},
new Catalog
{
Artikels = new List<Artikel>
{
new Artikel{PosNr=1}, new Artikel{PosNr=2}, new Artikel{PosNr=6}
}
}
}
};
int max=1;
try
{
max = order.Catalogen
.Where(c => c.Artikels.Count > 0)
.Max(c => c.Artikels.Max(a => a.PosNr)) + 1;
}
catch(Exception Ex)
{
max = 1;
}
Console.WriteLine (max);
}
class Artikel {
public int PosNr;
};
class Catalog {
public List<Artikel> Artikels;
};
class Order {
public List<Catalog> Catalogen;
}
Is there a more simple way to get the max posnr taking into account that an ordercatalog can be empty ? The where seems to be needed to consider this fact but it makes the code look clunchy so I am looking for a better way.
var q = from cataloog in order.Catalogen
from artikel in cataloog.Artikels
select artikel.Posnr;
var max = q.Max() + 1;
Alternatively
var max = order.Catalogen.SelectMany(c => c.Artikels).Max(a => a.Posnr) + 1;
Update:
Of course, if there are no Artikels, than the maximum Posnr is undefined, which is reported by Enumerable.Max as an InvalidOperationException.
In your specific case, there is an easy solution for that:
var max = order.Catalogen.SelectMany(c => c.Artikels)
.Select(a => a.Posnr)
.DefaultIfEmpty()
.Max() + 1;
Greetings Please keep in mind there is no database and these are fake functions to make the component work for testing, i have a List which makes a 24 hours based on 15 minutes scale and produces from this method:
public List<ScaleLine> GetHoursAndScales(ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
int _scale = Convert.ToInt32(scale);
int _count = _scale * 24;
int _scaleCount = 60 / _scale;
List<ScaleLine> _list = new List<ScaleLine>();
var start = DateTime.Today;
var clockQuery = from offset in Enumerable.Range(1, _count)
select TimeSpan.FromMinutes(_scaleCount * offset);
foreach (var time in clockQuery)
{
_list.Add(new ScaleLine() { Id = _list.Count, Hours = (start + time).ToString("HH:mm"), Scale = _scale });
}
return _list;
}
And i have another list which is called Reserved hours which is produces on this method:
public List<Reservedhours> AllReservedHours()
{
return new List<Reservedhours>
{
new Reservedhours() { Id = 1, Date = DateTime.Now, StartPoint = "08:00", EndPoint = "10:00" },
new Reservedhours() { Id = 2, Date = DateTime.Now, StartPoint = "14:00", EndPoint = "16:00" },
new Reservedhours() { Id = 3, Date = DateTime.Now, StartPoint = "20:00", EndPoint = "22:00" },
new Reservedhours() { Id = 4, Date = DateTime.Now.AddDays(1), StartPoint = "07:00", EndPoint = "11:00" },
new Reservedhours() { Id = 5, Date = DateTime.Now.AddDays(1), StartPoint = "13:00", EndPoint = "15:00" },
new Reservedhours() { Id = 6, Date = DateTime.Now.AddDays(1), StartPoint = "15:00", EndPoint = "18:00" },
new Reservedhours() { Id = 7, Date = DateTime.Now.AddDays(1), StartPoint = "18:00", EndPoint = "22:00" },
};
}
Now i have another list that produces the available hours based on reserved hours and first list that produces 24 hours:
public List<ScaleLine> GetAvailableHours(DateTime date, ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
List<Reservedhours> _timeLine = AllReservedHours().Where(x => x.Date.Date == date.Date)
.Select( a => new Reservedhours {StartPoint = a.StartPoint, EndPoint = a.EndPoint } )
.ToList();
List<ScaleLine> _available = GetHoursAndScales();
//scale convert:
int _scale = Convert.ToInt32(scale);
foreach (var _item in _timeLine)
{
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault().Id;
//Convert to datetime
DateTime _opening = DateTime.ParseExact(_item.StartPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
DateTime _closing = DateTime.ParseExact(_item.EndPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
//Getting duration time
TimeSpan duration = _closing.Subtract(_opening);
double _duration = duration.TotalMinutes;
//getting coverage
int timeScale = 60 / _scale;
int coverage = Convert.ToInt32(_duration) / timeScale;
//remove unavailable timespots
_available.RemoveRange(index, coverage);
}
return _available;
}
But problem is when the Foreach loop starts it removes the first range correctly based on its index, lets say if _available list has 96 members it removes 8 of them, so second time it should have 88 members and find the index within those 88 members but it doesn't and gets the index wrong (it takes the index as if the list still had 96 members) and so goes for every other action within the loop. how can i fix this issue? is there a way i can get the available list without doing a foreach loop?
The problem is your determination of the index. Instead of asking the list for the index of the desired object, you ask for the property value of an object and using this as index:
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault()
.Id;
Either you ask really for the index by calling IndexOf():
var matchingItem = _available.Where(x => x.Hours == _item.StartPoint)
.First();
var index = _available.IndexOf(matchingItem);
Or you replace your .RemoveRange() by something else, that really removes your desired elements.
I have the following code:
Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
Is it possible to convert ContinueWhenAll to a synchronous method? I want to switch back between async and sync.
Edit: I should metnion that each of the "tasks" in the continuewhenall method should be executing synchronously.
If you want to leave your existing code intact and have a variable option of executing synchronously you should make these changes:
bool isAsync = false; // some flag to check for async operation
var batch = Task.Factory.ContinueWhenAll(items.Select(p =>
{
return CreateItem(p);
}).ToArray(), completedTasks => { Console.WriteLine("completed"); });
if (!isAsync)
batch.Wait();
This way you can toggle it programmatically instead of by editing your source code. And you can keep the continuation code the same for both methods.
Edit:
Here is a simple pattern for having the same method represented as a synchronous and async version:
public Item CreateItem(string name)
{
return new Item(name);
}
public Task<Item> CreateItemAsync(string name)
{
return Task.Factory.StartNew(() => CreateItem(name));
}
Unless am mistaken this is what you're looking for
Task.WaitAll(tasks);
//continuation code here
i think you can try this.
using TaskContinuationOptions for a simple scenario.
var taskFactory = new TaskFactory(TaskScheduler.Defau
var random = new Random();
var tasks = Enumerable.Range(1, 30).Select(p => {
return taskFactory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Console.WriteLine(#"* 2: ID = " + t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}).ToArray();
Task.WaitAll(tasks);
or using TPL Dataflow for a complex scenario.
var step2 = new ActionBlock<int>(i => {
Thread.Sleep(i);
Console.WriteLine(#"* 2: ID = " + i);
}, new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism = 1,
//MaxMessagesPerTask = 1
});
var random = new Random();
var tasks = Enumerable.Range(1, 50).Select(p => {
return Task.Factory.StartNew(() => {
var timeout = random.Next(5, p * 50);
Thread.Sleep(timeout / 2);
Console.WriteLine(#" 1: ID = " + p);
return p;
}).ContinueWith(t => {
Thread.Sleep(t.Result);
step2.Post(t.Result);
});
}).ToArray();
await Task.WhenAll(tasks).ContinueWith(t => step2.Complete());
await step2.Completion;